<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue + Cesium WebSocket实时轨迹播放</title>
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <script src="/Cesium/Cesium.js"></script>
    <link href="/Cesium/Widgets/widgets.css" rel="stylesheet">
    <style>
        body { margin: 0; padding: 0; font-family: Arial, sans-serif; }
        #app { width: 100%; height: 100vh; }
        #cesiumContainer { width: 100%; height: 100vh; }
        .info {
            position: absolute; top: 10px; left: 10px; z-index: 1000;
            background: rgba(0,0,0,0.8); color: #fff; padding: 14px; border-radius: 6px; max-width: 520px;
        }
        .row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 8px; }
        .row button { background: #4CAF50; color: #fff; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; }
        .row button:hover { background: #45a049; }
        .row button.danger { background: #e84c3d; }
        .row button.danger:hover { background: #cf3e30; }
        .row button:disabled { background: #666; cursor: not-allowed; }
        .row select, .row input { padding: 4px 6px; font-size: 12px; }
        .kv { font-size: 12px; line-height: 1.6; }
        .kv b { color: #ffd666; }
        
        .env-selector {
            margin: 10px 0;
            padding: 8px;
            background: rgba(0, 0, 0, 0.3);
            border-radius: 4px;
            border: 1px solid #444;
        }
        
        .env-selector label {
            color: #fff;
            font-size: 12px;
            font-weight: bold;
        }
        
        .env-selector select {
            margin-left: 8px;
            padding: 4px 8px;
            background: #333;
            color: #fff;
            border: 1px solid #666;
            border-radius: 3px;
            font-size: 12px;
        }
        
        .env-selector select:focus {
            outline: none;
            border-color: #4CAF50;
        }
        .timeline { display:flex; align-items:center; gap:8px; margin-top:6px; }
        .timeline input[type="range"] { width: 240px; }
        
        /* 右侧面板样式 */
        .right-panel {
            position: absolute; top: 10px; right: 10px; z-index: 1000;
            width: 600px; max-height: 90vh; overflow-y: auto;
        }
        
        /* 任务列表表格样式 */
        .task-table {
            background: rgba(0,0,0,0.9); color: #fff; padding: 14px; border-radius: 6px;
            margin-bottom: 10px;
        }
        .task-table h3 { margin: 0 0 12px 0; color: #ffd666; font-size: 14px; }
        .task-table table { width: 100%; border-collapse: collapse; font-size: 11px; table-layout: fixed; }
                 .task-table th, .task-table td { 
             padding: 6px 8px; text-align: left; border-bottom: 1px solid #333; 
         }
         
         .task-table td { 
             overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
         }
         
         .task-table th { 
             overflow: visible; white-space: nowrap; word-wrap: normal;
         }
                 .task-table th:nth-child(1), .task-table td:nth-child(1) { width: 18%; } /* 任务名称 */
         .task-table th:nth-child(2), .task-table td:nth-child(2) { width: 12%; } /* 开始时间 */
         .task-table th:nth-child(3), .task-table td:nth-child(3) { width: 12%; } /* 预计完成时间 */
         .task-table th:nth-child(4), .task-table td:nth-child(4) { width: 20%; } /* 起点 */
         .task-table th:nth-child(5), .task-table td:nth-child(5) { width: 20%; } /* 终点 */
         .task-table th:nth-child(6), .task-table td:nth-child(6) { width: 10%; } /* 预计里程 */
         .task-table th:nth-child(7), .task-table td:nth-child(7) { width: 8%; } /* 预计时长 */
        .task-table th { background: rgba(255,255,255,0.1); font-weight: bold; }
                 .task-table tr:hover { background: rgba(255,255,255,0.05); }
         
         .selected-task {
             background: rgba(255, 0, 0, 0.3) !important;
             border-left: 3px solid #ff0000;
         }
         
         .selected-task:hover {
             background: rgba(255, 0, 0, 0.4) !important;
         }
        .task-table .completed-task { 
            opacity: 0.6; 
            background: rgba(76, 175, 80, 0.1); 
        }
        
        /* 无任务数据样式 */
        .no-tasks {
            background: rgba(0,0,0,0.9); color: #fff; padding: 14px; border-radius: 6px;
            text-align: center;
        }
        .no-tasks p {
            margin: 0; color: #999; font-size: 12px;
        }
        
        /* 轨迹属性面板样式 */
        .track-properties-panel {
            background: rgba(0,0,0,0.9); color: #fff; padding: 14px; border-radius: 6px;
            margin-top: 10px;
        }
        .track-properties-header {
            display: flex; justify-content: space-between; align-items: center;
            margin-bottom: 12px;
        }
        .track-properties-header h3 {
            margin: 0; color: #ffd666; font-size: 14px;
        }
        .track-selector {
            background: rgba(255,255,255,0.1); color: #fff; border: 1px solid #333;
            padding: 4px 8px; border-radius: 3px; font-size: 11px;
            min-width: 150px;
        }
        .track-selector option {
            background: #333; color: #fff;
        }
        .track-properties-content {
            font-size: 11px;
        }
        .property-group {
            margin-bottom: 12px;
        }
        .property-group h4 {
            margin: 0 0 6px 0; color: #4CAF50; font-size: 12px;
            border-bottom: 1px solid #333; padding-bottom: 2px;
        }
        .property-item {
            display: flex; justify-content: space-between; align-items: center;
            padding: 3px 0; border-bottom: 1px solid rgba(255,255,255,0.1);
        }
        .property-label {
            color: #ccc; font-weight: bold; min-width: 80px;
        }
        .property-value {
            color: #fff; text-align: right; flex: 1;
        }
        .no-track-data, .no-track-selected {
            text-align: center; padding: 20px 0;
        }
        .no-track-data p, .no-track-selected p {
            margin: 0; color: #999; font-size: 12px;
        }
        
                 .track-points-list {
             max-height: 300px;
             overflow-y: auto;
             border: 1px solid #444;
             border-radius: 4px;
             background: rgba(0, 0, 0, 0.3);
         }
         
         .track-point-header {
             display: flex;
             justify-content: space-between;
             align-items: center;
             padding: 8px 12px;
             background: rgba(255, 255, 255, 0.1);
             border-bottom: 2px solid #666;
             font-size: 12px;
             font-weight: bold;
             color: #fff;
             position: sticky;
             top: 0;
             z-index: 1;
         }
        
        .track-point-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 8px 12px;
            border-bottom: 1px solid #333;
            font-size: 12px;
            line-height: 1.4;
        }
        
        .track-point-item:last-child {
            border-bottom: none;
        }
        
        .track-point-item:hover {
            background: rgba(255, 255, 255, 0.1);
        }
        
                 .point-index {
             flex: 0.5;
             color: #FFD700;
             font-weight: bold;
             text-align: center;
         }
         
         .point-time {
             flex: 1;
             color: #4CAF50;
             font-weight: bold;
         }
        
        .point-location {
            flex: 2;
            color: #2196F3;
        }
        
        .point-altitude {
            flex: 1;
            color: #FF9800;
            text-align: center;
        }
        
        .point-speed {
            flex: 1;
            color: #9C27B0;
            text-align: center;
        }
        
        .point-battery {
            flex: 1;
            color: #F44336;
            text-align: center;
        }
        
        /* GeoJSON表格样式 */
        .geojson-table {
            background: rgba(0,0,0,0.9); color: #fff; padding: 14px; border-radius: 6px;
            max-height: 60vh; overflow-y: auto; margin-top: 10px;
        }
        .geojson-table h3 { margin: 0 0 12px 0; color: #ffd666; font-size: 14px; }
        .geojson-table table { width: 100%; border-collapse: collapse; font-size: 11px; }
        .geojson-table th, .geojson-table td { 
            padding: 6px 8px; text-align: left; border-bottom: 1px solid #333; 
        }
        .geojson-table th { background: rgba(255,255,255,0.1); font-weight: bold; }
        .geojson-table tr:hover { background: rgba(255,255,255,0.05); }
        .geojson-table .actions { display: flex; gap: 4px; }
        .geojson-table .actions button { 
            padding: 2px 6px; font-size: 10px; background: #2196F3; 
            border: none; color: white; border-radius: 3px; cursor: pointer; 
        }
        .geojson-table .actions button:hover { background: #1976D2; }
        .geojson-table .actions button.danger { background: #f44336; }
        .geojson-table .actions button.danger:hover { background: #d32f2f; }
        .geojson-table .json-preview { 
            max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; 
        }
        
        /* 下拉菜单样式 */
        .dropdown {
            position: relative;
            display: inline-block;
        }
        
        .dropdown-toggle {
            background-color: #4CAF50 !important;
            color: white;
            border: none;
            cursor: pointer;
        }
        
        .dropdown-toggle:hover {
            background-color: #45a049 !important;
        }
        
        .dropdown-menu {
            display: none;
            position: absolute;
            background-color: #f9f9f9;
            min-width: 120px;
            box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
            z-index: 1000;
            border-radius: 4px;
            top: 100%;
            left: 0;
        }
        
        .dropdown:hover .dropdown-menu {
            display: block;
        }
        
        .dropdown-item {
            color: black;
            padding: 8px 12px;
            text-decoration: none;
            display: block;
            border: none;
            background: none;
            width: 100%;
            text-align: left;
            cursor: pointer;
            font-size: 12px;
        }
        
        .dropdown-item:hover {
            background-color: #f1f1f1;
        }
        
        /* 编辑对话框样式 */
        .edit-dialog-overlay {
            position: fixed; top: 0; left: 0; width: 100%; height: 100%;
            background: rgba(0,0,0,0.7); z-index: 2000; display: flex;
            align-items: center; justify-content: center;
        }
        
        .edit-dialog {
            background: #fff; border-radius: 8px; width: 80%; max-width: 800px;
            max-height: 80vh; overflow: hidden; display: flex; flex-direction: column;
        }
        
        .edit-dialog-header {
            background: #2196F3; color: white; padding: 15px 20px;
            display: flex; justify-content: space-between; align-items: center;
        }
        
        .edit-dialog-header h3 {
            margin: 0; font-size: 16px;
        }
        
        .close-btn {
            background: none; border: none; color: white; font-size: 24px;
            cursor: pointer; padding: 0; width: 30px; height: 30px;
            display: flex; align-items: center; justify-content: center;
        }
        
        .close-btn:hover {
            background: rgba(255,255,255,0.2); border-radius: 50%;
        }
        
        .edit-dialog-content {
            flex: 1; overflow-y: auto; padding: 20px;
        }
        
        .edit-table-container {
            max-height: 60vh; overflow-y: auto;
        }
        
        .edit-table {
            width: 100%; border-collapse: collapse; font-size: 12px;
        }
        
        .edit-table th, .edit-table td {
            padding: 8px 12px; text-align: left; border-bottom: 1px solid #ddd;
        }
        
        .edit-table th {
            background: #f5f5f5; font-weight: bold;
        }
        
        .edit-table tr:hover {
            background: #f9f9f9;
        }
        
        .edit-input {
            width: 100%; padding: 4px 8px; border: 1px solid #ddd;
            border-radius: 4px; font-size: 12px;
        }
        
        .edit-input:focus {
            outline: none; border-color: #2196F3;
        }
        
        .edit-dialog-footer {
            padding: 15px 20px; border-top: 1px solid #ddd;
            display: flex; gap: 10px; justify-content: flex-end;
        }
        
        .save-btn, .cancel-btn {
            padding: 8px 16px; border: none; border-radius: 4px;
            cursor: pointer; font-size: 12px;
        }
        
        .save-btn {
            background: #4CAF50; color: white;
        }
        
        .save-btn:hover {
            background: #45a049;
        }
        
        .cancel-btn {
            background: #f44336; color: white;
        }
        
        .cancel-btn:hover {
            background: #d32f2f;
        }
    </style>
</head>
<body>
<div id="app">
    <div id="cesiumContainer"></div>
    <div class="info">
        <!-- 环境选择器 -->
        <div class="env-selector">
            <label>环境：
                <select v-model="currentEnv" @change="onEnvChange">
                    <option value="dev">开发环境 (dev)</option>
                    <option value="test">测试环境 (test)</option>
                    <option value="prod">生产环境 (prod)</option>
                </select>
            </label>
        </div>
        <div class="kv">状态：<b>{{ status }}</b></div>
                 <div class="kv">WebSocket：<b :style="{color: isConnected ? '#4CAF50' : '#f44336'}">{{ isConnected ? '已连接' : '未连接' }}</b></div>
                   <div class="kv">轨迹数量：<b>{{ flightTracks.length }}</b></div>
          <div class="kv">航线数量：<b>{{ routeCount }}</b></div>
         <div class="kv">初始坐标：<b>{{ initialLongitude.toFixed(6) }}, {{ initialLatitude.toFixed(6) }}</b></div>
                 <div class="row">
             <label>选择轨迹：
                 <select v-model.number="currentTrackIndex" @change="setCurrentTrack(currentTrackIndex)">
                      <option v-for="(track, index) in flightTracks" :key="track.id" :value="index">
                          {{ getTaskNameByTrackId(track.id) }}
                      </option>
                 </select>
             </label>
             <label>vid: <input v-model.number="vid" style="width: 60px;" placeholder="684"></label>
             <label>vidStatus: <input v-model.number="vidStatus" style="width: 60px;" placeholder="2"></label>
             <button @click="sendSimulationMessage" :disabled="!isConnected" style="margin-left: 5px; padding: 2px 6px; font-size: 10px; background: #9C27B0; color: white; border: none; border-radius: 3px; cursor: pointer;">仿真验证</button>
                           <label>WebSocket地址：
                  <input v-model="websocketUrl" style="width: 200px;" placeholder="ws://127.0.0.1:6088/uam/v2/ws/uam/channel/245">
                  <button @click="reconnectWebSocket" :disabled="isConnected">重连</button>
             </label>
                             <label>获取航线：ClientType: <input v-model="wsClientType" style="width: 50px;" placeholder="1"> 方案Id: <input v-model="planId" style="width: 50px;" placeholder="1"> 需求Id: <input v-model="schemaRequirementId" style="width: 80px;" placeholder="815"> <button @click="sendInitMessage" :disabled="!isConnected">获取需求对应的航线</button> <button @click="updateBatchPlanIdFromInput" style="margin-left: 5px; padding: 2px 6px; font-size: 10px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer;">同步方案ID</button> <button @click="sendLandingFieldMessage" :disabled="!isConnected" style="margin-left: 5px; padding: 2px 6px; font-size: 10px; background: #FF9800; color: white; border: none; border-radius: 3px; cursor: pointer;">起降场</button> <button @click="sendTaskMessage" :disabled="!isConnected" style="margin-left: 5px; padding: 2px 6px; font-size: 10px; background: #2196F3; color: white; border: none; border-radius: 3px; cursor: pointer;">获取任务</button></label>
         </div>
                 <div class="row">
                          <button @click="focusFirstPoint">定位到起点</button>
              <button @click="focusToInitialLocation">定位到初始位置</button>
              <button @click="resetToInitialLocation">重置位置</button>
              <button @click="forceUpdateZoomLevel">更新缩放级别</button>
                             <button @click="clearAllTracks" class="danger">清除所有轨迹</button>
               <button @click="clearHistoricalTracks" class="danger">清除历史轨迹</button>
               <button @click="clearAllRoutes" class="danger">清除所有航线</button>
                         <label>渲染间隔：
                 <select v-model.number="renderDelay" @change="updateRenderDelay">
                     <option :value="500">0.5秒</option>
                     <option :value="1000">1秒</option>
                     <option :value="2000">2秒</option>
                     <option :value="5000">5秒</option>
                </select>
             </label>
             <label>更新节流：
                 <select v-model.number="updateThrottle" @change="updateRenderDelay">
                     <option :value="200">0.2秒</option>
                     <option :value="500">0.5秒</option>
                     <option :value="1000">1秒</option>
                 </select>
             </label>
             <label>实时更新：
                 <input type="checkbox" v-model="enableRealTimeUpdate" @change="toggleRealTimeUpdate">
             </label>
                           <label>历史轨迹：
                  <input type="checkbox" v-model="showHistoricalTracks" @change="toggleHistoricalTracks">
              </label>
              <label>分段颜色：
                  <input type="checkbox" v-model="enableSegmentedColors">
            </label>
        </div>
        <div class="row">
            <button @click="startDrawPolygon" :disabled="drawing">开始绘制多边形</button>
            <button @click="finishDrawPolygon" :disabled="!drawing">完成</button>
            <button @click="clearDrawPolygon" class="danger">清空多边形</button>
                           <span style="font-size:12px;color:#ccc">提示：左键落点、移动预览、右键完成。地图滚动后不会自动重新定位。</span>
        </div>
         
         <!-- 时间播放方案选择 -->
         <div class="row">
             <label>时间播放方案：
                 <select v-model="timeMode" @change="applyTimeMode">
                     <option value="custom">方案A：自定义时间播放</option>
                     <option value="cesium">方案B：Cesium内置时间播放</option>
                 </select>
             </label>
         </div>
         
         <!-- 时间轴控制 -->
         <div class="row">
             <button @click="playPause" :disabled="!hasValidTrack">{{ playing ? '暂停' : '播放' }}</button>
             <button @click="stepForward" :disabled="!hasValidTrack">向前</button>
             <button @click="stepBackward" :disabled="!hasValidTrack">向后</button>
             <button @click="resetTime" :disabled="!hasValidTrack">重置</button>
             <label>播放速度：
                 <select v-model.number="playbackSpeed" @change="updatePlaybackSpeed">
                     <option :value="0.5">0.5x</option>
                     <option :value="1">1x</option>
                     <option :value="2">2x</option>
                     <option :value="5">5x</option>
                     <option :value="10">10x</option>
                 </select>
             </label>
         </div>
         
         <!-- 时间轴滑块 -->
         <div class="timeline" v-if="hasValidTrack && timeMode === 'custom'">
             <span style="font-size:12px;">时间轴：</span>
            <input type="range"
                    v-model.number="currentTimePercent" 
                    @input="seekToTime"
                    min="0" 
                    max="100" 
                   step="0.1"
                    style="width: 200px;">
             <span style="font-size:12px;">{{ formatTime(currentTime) }} / {{ formatTime(totalDuration) }}</span>
        </div>

                 <div class="kv">当前位置：<b>{{ posText }}</b></div>
         <div class="kv">当前区段：<b>{{ segText }}</b></div>
         <div class="kv">飞行方向：<b>{{ headingText }}</b></div>
         <div class="kv">地图缩放：<b>{{ currentZoomLevelDisplay }}级</b></div>
         <div class="kv">定位状态：<b :style="{color: initialLocationSet ? '#4CAF50' : '#ff9800'}">{{ initialLocationSet ? '已定位' : '定位中...' }}</b></div>
                  <div class="kv">轨迹统计：<b>{{ trackStats }}</b></div>
         <div class="kv">当前方案ID：<b>{{ planId }}</b></div>
         
         <!-- GeoJSON记录表格 -->
         <div class="geojson-table" style="position: static; width: 100%; max-width: none; margin-top: 10px;">
             <h3>GeoJSON记录 ({{ geojsonRecords.length }})</h3>
             <div v-if="geojsonRecords.length > 0" style="margin-bottom: 10px;">
                 <label>批量设置方案ID：
                     <input v-model.number="batchPlanId" 
                            type="number" 
                            min="1" 
                            style="width: 60px; padding: 2px; font-size: 12px;"
                            placeholder="1">
                     <button @click="setBatchPlanId" 
                             style="margin-left: 5px; padding: 2px 6px; font-size: 10px; background: #2196F3; color: white; border: none; border-radius: 3px; cursor: pointer;">
                         应用
                     </button>
                 </label>
                 <label style="margin-left: 20px;">批量设置名称前缀：
                     <input v-model="batchNamePrefix" 
                            type="text" 
                            style="width: 80px; padding: 2px; font-size: 12px;"
                            placeholder="多边形">
                     <button @click="setBatchNames" 
                             style="margin-left: 5px; padding: 2px 6px; font-size: 10px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer;">
                         应用
                     </button>
                 </label>
             </div>
             <table v-if="geojsonRecords.length > 0">
                 <thead>
                     <tr>
                         <th>序号</th>
                         <th>名称</th>
                         <th>时间</th>
                         <th>点数</th>
                         <th>方案ID</th>
                         <th>操作</th>
                     </tr>
                 </thead>
                 <tbody>
                     <tr v-for="(record, index) in geojsonRecords" :key="index">
                         <td>{{ index + 1 }}</td>
                         <td>
                             <input v-model="record.name" 
                                    type="text" 
                                    style="width: 80px; padding: 2px; font-size: 12px;"
                                    :placeholder="`多边形${index + 1}`"
                                    title="记录名称">
                         </td>
                         <td>{{ formatTime(record.timestamp) }}</td>
                         <td>{{ record.pointCount }}</td>
                         <td>
                             <input v-model="record.planId" 
                                    type="number" 
                                    min="1" 
                                    style="width: 60px; padding: 2px; font-size: 12px;"
                                    placeholder="1"
                                    title="方案ID">
                         </td>
                         <td class="actions">
                             <button @click="copyRecordGeoJSON(index)" title="复制GeoJSON">复制</button>
                             <button @click="viewRecordGeoJSON(index)" title="查看完整GeoJSON">查看</button>
                             <button @click="deleteRecord(index)" class="danger" title="删除记录">删除</button>
                             <div class="dropdown">
                                 <button class="btn-small dropdown-toggle">导调</button>
                                 <div class="dropdown-menu">
                                     <button @click="sendLocalAvoidance(record)" class="dropdown-item">局部避障</button>
                                 </div>
                             </div>
                         </td>
                     </tr>
                 </tbody>
             </table>
             <div v-else style="text-align: center; color: #999; padding: 20px;">
                 暂无绘制记录
             </div>
         </div>
     </div>
     
     <!-- 右侧面板 -->
     <div class="right-panel">
         <!-- 任务列表面板 -->
         <div class="task-table" v-if="taskList.length > 0">
             <h3>任务列表 ({{ taskList.length }})</h3>
             <table>
                 <thead>
                     <tr>
                         <th>任务名称</th>
                         <th>开始时间</th>
                         <th>预计完成时间</th>
                         <th>起点</th>
                         <th>终点</th>
                         <th>预计里程</th>
                         <th>预计时长</th>
                     </tr>
                 </thead>
                 <tbody>
                     <tr v-for="(task, index) in taskList" :key="index" :class="{ 
                         'completed-task': taskStatus[task.uuid]?.completed,
                         'selected-task': isTaskSelected(task.uuid)
                     }">
                         <td>{{ task.name }}</td>
                         <td>{{ formatTaskTime(task.departureTime) }}</td>
                         <td>{{ formatTaskTime(task.endTime) }}</td>
                         <td>{{ task.origin }}</td>
                         <td>{{ task.destination }}</td>
                         <td>{{ task.mileage || 'N/A' }}</td>
                         <td>{{ task.duration || 'N/A' }}</td>
                     </tr>
                 </tbody>
             </table>
         </div>
         <div v-else class="no-tasks">
             <p>暂无任务数据</p>
         </div>
         
         <!-- 轨迹属性面板 -->
         <div class="track-properties-panel">
         <div class="track-properties-header">
             <h3>轨迹属性</h3>
             <select v-model="selectedTrackId" @change="onTrackSelectionChange" class="track-selector">
                 <option value="">请选择轨迹</option>
                 <option v-for="track in flightTracks" :key="track.id" :value="track.id">
                     {{ getTaskNameByTrackId(track.id) }}
                 </option>
             </select>
         </div>
         <div class="track-properties-content" v-if="selectedTrackId && selectedTrackData">
             <div class="property-group" v-if="selectedTrackData">
                 <h4>基本信息</h4>
                 <div class="property-item">
                     <span class="property-label">轨迹ID:</span>
                     <span class="property-value">{{ selectedTrackData.id }}</span>
                 </div>
                 <div class="property-item">
                     <span class="property-label">飞行器类型:</span>
                     <span class="property-value">{{ selectedTrackData.aircraft_type || '未知' }}</span>
                 </div>
                 <div class="property-item">
                     <span class="property-label">任务类型:</span>
                     <span class="property-value">{{ selectedTrackData.mission_type || '未知' }}</span>
                 </div>
                 <div class="property-item">
                     <span class="property-label">轨迹点数:</span>
                     <span class="property-value">{{ selectedTrackData.points ? selectedTrackData.points.length : 0 }}</span>
                 </div>
             </div>
             
             <div class="property-group" v-if="recentTrackPoints && recentTrackPoints.length > 0">
                 <h4>当前位置 {{ getCurrentDate() }}</h4>
                 <div class="track-points-list">
                     <div class="track-point-header">
                         <span class="point-index">序号</span>
                         <span class="point-time">时间</span>
                         <span class="point-location">位置</span>
                         <span class="point-altitude">高度</span>
                         <span class="point-speed">速度</span>
                         <span class="point-battery">电池</span>
                     </div>
                     <div class="track-point-item" v-for="(point, index) in recentTrackPoints" :key="index">
                         <span class="point-index">{{ getPointIndex(point) }}</span>
                         <span class="point-time">{{ point.timestamp_absolute ? formatTimeOnly(point.timestamp_absolute) : 'N/A' }}</span>
                         <span class="point-location">{{ point.longitude ? point.longitude.toFixed(6) : 'N/A' }}, {{ point.latitude ? point.latitude.toFixed(6) : 'N/A' }}</span>
                         <span class="point-altitude">{{ point.altitude || 'N/A' }}</span>
                         <span class="point-speed">{{ point.speed || 'N/A' }}</span>
                         <span class="point-battery">{{ point.battery || 'N/A' }}</span>
                     </div>
                 </div>
             </div>
             

         </div>
         <div v-else-if="selectedTrackId" class="no-track-data">
             <p>未找到轨迹数据</p>
         </div>
         <div v-else class="no-track-selected">
             <p>请选择轨迹查看属性</p>
         </div>
     </div>
     </div>
     
     <!-- 编辑对话框 -->
     <div v-if="showEditDialogFlag" class="edit-dialog-overlay" @click="closeEditDialog">
         <div class="edit-dialog" @click.stop>
             <div class="edit-dialog-header">
                 <h3>{{ currentEditTitle }}</h3>
                 <button class="close-btn" @click="closeEditDialog">&times;</button>
             </div>
             <div class="edit-dialog-content">
                 <div class="edit-table-container">
                     <table class="edit-table">
                         <thead>
                             <tr>
                                 <th>属性名</th>
                                 <th>属性值</th>
                             </tr>
                         </thead>
                         <tbody>
                             <tr v-for="(value, key) in editedExtraAttributes" :key="key">
                                 <td>{{ key }}</td>
                                 <td>
                                     <input v-model="editedExtraAttributes[key]" 
                                            type="text" 
                                            class="edit-input"
                                            :placeholder="value">
                                 </td>
                             </tr>
                         </tbody>
                     </table>
                 </div>
             </div>
             <div class="edit-dialog-footer">
                 <button @click="saveEditDialog" class="save-btn">修改</button>
                 <button @click="closeEditDialog" class="cancel-btn">取消</button>
             </div>
         </div>
     </div>
</div>

<script>
const { createApp } = Vue;
createApp({
    data() {
        return {
            status: '正在初始化...',
            viewer: null,
            entity: null,
            trackEntity: null,
            positionProp: null,
            start: null,
            end: null,
            playing: false,
            speed: 1,
            jumpSec: 0,
            currentSec: 0,
            endSeconds: 0,
                         posText: '-',
             segText: '-',
             headingText: '-',
            timeMode: 'custom', // 默认使用自定义时间播放
            isSeeking: false,
                         // WebSocket相关
             websocket: null,
             websocketUrl: 'ws://127.0.0.1:6088/uam/v2/ws/uam/channel/245', // WebSocket服务器地址
             isConnected: false,
             reconnectInterval: null,
             reconnectAttempts: 0,
             maxReconnectAttempts: 5,
             wsClientType: '1', // WebSocket客户端类型
             planId: '1', // 计划ID
             schemaRequirementId: '815', // 需求ID
            
            // 实时轨迹数据
            flightTracks: [], // 将从WebSocket获取
            trackColors: [Cesium.Color.RED, Cesium.Color.BLUE, Cesium.Color.GREEN, Cesium.Color.YELLOW],
            trackEntities: {}, // 存储轨迹实体 {trackId: entity}
            aircraftEntities: {}, // 存储飞机实体 {trackId: entity}
            trackPolylines: {}, // 存储轨迹线实体 {trackId: entity}
            
                         // 实时渲染相关
             realTimeMode: true, // 实时模式
             renderInterval: null,
             lastRenderTime: 0,
             renderDelay: 2000, // 每2秒渲染一次新点
             lastUpdateTime: 0, // 上次更新时间
             updateThrottle: 500, // 更新节流间隔（毫秒）
             enableRealTimeUpdate: true, // 是否启用实时更新
                          showHistoricalTracks: true, // 是否显示历史轨迹
             enableSegmentedColors: true, // 是否启用分段颜色（飞过的轨迹用青色）
             // 初始定位坐标
             initialLongitude: 120.16196180060605,
             initialLatitude: 30.14332020097707,
             initialZoomLevel: 13,
             currentZoomLevel: 13, // 当前地图缩放级别
             initialLocationSet: false, // 是否已经完成初始定位
             zoomUpdateTimeout: null, // 缩放级别更新防抖定时器
            // 当前选中的轨迹（用于播放）
            currentTrackIndex: 0,
            flightPath: [], // 将根据选中的轨迹动态设置
             
             // 时间轴控制相关
             playing: false, // 是否正在播放
             playbackSpeed: 1, // 播放速度
             currentTime: 0, // 当前时间（秒）
             totalDuration: 0, // 总时长（秒）
             currentTimePercent: 0, // 当前时间百分比
             playbackInterval: null, // 播放定时器
                         // 为每条轨迹创建独立的飞机实体
             aircraftEntities: [], // 存储每条轨迹的飞机实体
             // 绘制相关
             drawing: false,
             drawPositions: [], // Cartesian3
             drawPolygonEntity: null,
             drawOutlineEntity: null,
             drawHandler: null,
             polygonGeoJSON: null, // 存储多边形的GeoJSON
             geojsonRecords: [], // 存储所有GeoJSON记录
             batchPlanId: 1, // 批量设置方案ID
             batchNamePrefix: '多边形', // 批量设置名称前缀
             lastHeading: null, // 存储上一次的飞行方向，用于平滑处理
             taskList: [], // 任务列表数据
             taskStatus: {}, // 任务状态跟踪 {uuid: {completed: boolean, aircraftEntity: entity}}
             landingFieldEntities: {}, // 起降场实体
             globalClickHandler: null, // 全局点击事件处理器
             showEditDialogFlag: false, // 是否显示编辑对话框
             currentEditData: null, // 当前编辑的数据
             currentEditTitle: '', // 当前编辑的标题
             editedExtraAttributes: {}, // 编辑中的extraAttributes数据
             vid: 684, // 仿真验证vid
             vidStatus: 2, // 仿真验证状态
             arrowLineMaterial: null, // 缓存的箭头线条材质
             seekDebounceTimer: null, // 时间轴拖动防抖定时器
             selectedTrackId: '', // 选中的轨迹ID
             selectedTrackData: null, // 选中的轨迹数据
             currentTrackPoint: null, // 当前轨迹点数据
             secondTrackData: null, // 第二个轨迹数据
             previousTrackPoint: null, // 上一个轨迹点数据
             thirdLastTrackPoint: null, // 倒数第三个轨迹点数据
             fourthLastTrackPoint: null, // 倒数第四个轨迹点数据
             recentTrackPoints: [], // 最近10个轨迹点
             isRequestingTasks: false, // 是否正在请求任务列表
             isUpdatingSelection: false, // 防止循环调用的标志
             currentEnv: 'test', // 当前环境，默认选中test
             apiBaseUrl: '' // 接口基础地址
        };
    },
         computed: { 
         clock() { return this.viewer ? this.viewer.clock : null; },
         trackStats() {
             if (this.flightTracks.length === 0) return '暂无轨迹';
             
             const totalPoints = this.flightTracks.reduce((sum, track) => 
                 sum + (track.points ? track.points.length : 0), 0
             );
             
             const activeTracks = this.flightTracks.filter(track => {
                 if (!track.points || track.points.length === 0) return false;
                 const currentTime = Date.now();
                 const trackEndTime = track.startTime + track.points[track.points.length - 1].timestamp * 1000;
                 return currentTime <= trackEndTime + 60000; // 1分钟内的轨迹认为是活跃的
             }).length;
             
             return `${this.flightTracks.length}条轨迹，${totalPoints}个点，${activeTracks}条活跃`;
         },
                   hasValidTrack() {
              return this.flightTracks.length > 0 && 
                     this.currentTrackIndex >= 0 && 
                     this.currentTrackIndex < this.flightTracks.length &&
                     this.flightTracks[this.currentTrackIndex] &&
                     this.flightTracks[this.currentTrackIndex].points &&
                     this.flightTracks[this.currentTrackIndex].points.length > 0;
          },
                     currentZoomLevelDisplay() {
               return this.currentZoomLevel;
           },
           routeCount() {
               return this.routeEntities ? Object.keys(this.routeEntities).length : 0;
           }
     },
    mounted() { 
        this.initCesium(); 
        this.updateWebSocketUrl(); // 根据环境设置WebSocket地址
        this.connectWebSocket();
    },
                   beforeUnmount() {
          this.disconnectWebSocket();
          if (this.renderInterval) {
              clearInterval(this.renderInterval);
          }
          if (this.playbackInterval) {
              clearInterval(this.playbackInterval);
          }
          if (this.zoomUpdateTimeout) {
              clearTimeout(this.zoomUpdateTimeout);
          }
          if (this.seekDebounceTimer) {
              clearTimeout(this.seekDebounceTimer);
          }
          // 清理全局点击事件处理器
          if (this.globalClickHandler) {
              this.globalClickHandler.destroy();
              this.globalClickHandler = null;
          }
      },
    methods: {
        // 安全缩放到目标：兼容 Promise / 非 Promise 返回
        safeZoomTo(target) {
            try {
                const res = this.viewer.zoomTo(target);
                if (res && typeof res.then === 'function') {
                    res.catch(() => {
                        this.viewer.camera.setView({
                            destination: Cesium.Cartesian3.fromDegrees(116.4074, 39.9042, 2000000),
                            orientation: { heading: 0.0, pitch: -Cesium.Math.PI_OVER_TWO, roll: 0.0 }
                        });
                    });
                }
            } catch (_) {
                this.viewer.camera.setView({
                    destination: Cesium.Cartesian3.fromDegrees(116.4074, 39.9042, 2000000),
                    orientation: { heading: 0.0, pitch: -Cesium.Math.PI_OVER_TWO, roll: 0.0 }
                });
            }
        },
        // 将相机定位到轨迹第一个点位置
        focusFirstPoint() {
            if (!this.flightPath || this.flightPath.length === 0) return;
            const p0 = this.flightPath[0];
             
             // 验证坐标的有效性
             if (p0 && 
                 typeof p0.longitude === 'number' && !isNaN(p0.longitude) &&
                 typeof p0.latitude === 'number' && !isNaN(p0.latitude) &&
                 p0.longitude >= -180 && p0.longitude <= 180 &&
                 p0.latitude >= -90 && p0.latitude <= 90) {
                 
            const height = Math.max(30000, (p0.altitude || 0) + 80000);
                 try {
            this.viewer.camera.setView({
                destination: Cesium.Cartesian3.fromDegrees(p0.longitude, p0.latitude, height),
                orientation: { heading: 0.0, pitch: -Cesium.Math.PI_OVER_TWO + 0.2, roll: 0.0 }
            });
                     
                     // 更新状态
                     this.status = `已定位到轨迹起点: ${p0.longitude.toFixed(4)}, ${p0.latitude.toFixed(4)}`;
                 } catch (e) {
                     console.warn('定位到起点失败:', p0, e);
                     this.status = '定位到起点失败';
                 }
             }
        },
        
        // 定位到第一条轨迹的第一个点
        focusToFirstTrackPoint() {
            if (this.flightTracks.length === 0) return;
            
            const firstTrack = this.flightTracks[0];
            if (!firstTrack.points || firstTrack.points.length === 0) return;
            
            const firstPoint = firstTrack.points[0];
            
            // 验证坐标的有效性
            if (firstPoint && 
                typeof firstPoint.longitude === 'number' && !isNaN(firstPoint.longitude) &&
                typeof firstPoint.latitude === 'number' && !isNaN(firstPoint.latitude) &&
                firstPoint.longitude >= -180 && firstPoint.longitude <= 180 &&
                firstPoint.latitude >= -90 && firstPoint.latitude <= 90) {
                
                const height = Math.max(30000, (firstPoint.altitude || 0) + 80000);
                try {
                    this.viewer.camera.setView({
                        destination: Cesium.Cartesian3.fromDegrees(firstPoint.longitude, firstPoint.latitude, height),
                        orientation: { heading: 0.0, pitch: -Cesium.Math.PI_OVER_TWO + 0.2, roll: 0.0 }
                    });
                    
                    // 更新状态
                    this.status = `已定位到第一条轨迹起点: ${firstPoint.longitude.toFixed(4)}, ${firstPoint.latitude.toFixed(4)}`;
                } catch (e) {
                    console.warn('定位到第一条轨迹起点失败:', firstPoint, e);
                    this.status = '定位到第一条轨迹起点失败';
                }
            }
        },
        
        initCesium() {
            // 禁用Cesium ion功能以避免访问令牌问题
            Cesium.Ion.defaultAccessToken = '';
            
            // 或者使用一个简单的占位符令牌
            // Cesium.Ion.defaultAccessToken = 'placeholder-token';
            
            try {
                this.viewer = new Cesium.Viewer('cesiumContainer', {
                    // 禁用terrain以避免ion访问令牌问题
                    // terrain: Cesium.Terrain.fromWorldTerrain(),
                    animation: true, timeline: true,
                    baseLayerPicker: false, // 禁用图层选择器
                    geocoder: false, // 禁用地理编码器
                    homeButton: true,
                    sceneModePicker: true, navigationHelpButton: true,
                    fullscreenButton: true, infoBox: true, selectionIndicator: true
                });
                
                // 设置OpenStreetMap作为底图，不需要ion访问令牌
                this.viewer.imageryLayers.addImageryProvider(
                    new Cesium.OpenStreetMapImageryProvider({
                        url: 'https://a.tile.openstreetmap.org/'
                    })
                );
                
                // 设置时间轴和动画控制器的初始时间
                const startTime = Cesium.JulianDate.fromDate(new Date('2025-08-30T16:00:00Z'));
                const stopTime = Cesium.JulianDate.fromDate(new Date('2025-08-31T14:00:00Z'));
                
                this.viewer.clock.startTime = startTime;
                this.viewer.clock.stopTime = stopTime;
                this.viewer.clock.currentTime = startTime;
                this.viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
                this.viewer.clock.multiplier = 1;
                this.viewer.clock.shouldAnimate = false; // 初始状态为暂停
                
                // 设置时间轴范围
                this.viewer.timeline.zoomTo(startTime, stopTime);
                
                                 // 初始化播放速度
                 this.playbackSpeed = 1;
                 
                 // 应用时间播放方案
                 this.applyTimeMode();
                
                // 隐藏ion警告消息
                this.hideIonWarning();
                this.viewer.scene.globe.depthTestAgainstTerrain = false;
                
                 // 添加渲染错误处理
                 this.viewer.scene.renderError.addEventListener((scene, error) => {
                     console.error('Cesium渲染错误:', error);
                     this.status = '渲染错误，请检查数据';
                 });
                 
                 // 地图加载完成后定位到指定坐标
                 this.viewer.scene.globe.tileLoadProgressEvent.addEventListener((queuedTileCount) => {
                     if (queuedTileCount === 0 && !this.initialLocationSet) {
                         // 地图加载完成，定位到指定坐标
                         setTimeout(() => {
                             this.focusToInitialLocation();
                         }, 500);
                     }
                 });
                 
                 // 备用方案：延迟定位，确保地图完全加载
                 setTimeout(() => {
                     if (!this.initialLocationSet) {
                         this.focusToInitialLocation();
                     }
                 }, 3000);
                 
                 // 初始化实时渲染
                 this.initRealTimeRendering();

                this.clock.onTick.addEventListener(this.onTick);
                
                // 监听时钟变化，同步自定义时间播放
                this.viewer.clock.onTick.addEventListener((clock) => {
                    if (this.playing && this.hasValidTrack) {
                        // 计算当前时间相对于开始时间的秒数
                        const currentSeconds = Cesium.JulianDate.secondsDifference(clock.currentTime, clock.startTime);
                        if (currentSeconds >= 0 && currentSeconds <= this.totalDuration) {
                            this.currentTime = currentSeconds;
                            this.currentTimePercent = (this.currentTime / this.totalDuration) * 100;
                        }
                    }
                });
                 
                 // 监听相机变化，更新缩放级别显示
                 this.viewer.camera.changed.addEventListener(() => {
                     // 只有在初始定位完成后才更新缩放级别
                     if (this.initialLocationSet) {
                         // 防抖处理，避免频繁更新
                         if (this.zoomUpdateTimeout) {
                             clearTimeout(this.zoomUpdateTimeout);
                         }
                         this.zoomUpdateTimeout = setTimeout(() => {
                             this.updateCurrentZoomLevel();
                         }, 100);
                     }
                 });
                 
                 // 标记是否已经完成初始定位
                 this.initialLocationSet = false;
                 
                 // 添加全局点击事件处理器
                 this.addGlobalClickHandler();
                 
                 this.status = '地图初始化完成，正在定位到指定坐标...';
            } catch (e) { this.status = '初始化失败'; console.error(e); }
        },
        
         // 定位到初始位置
         focusToInitialLocation() {
             try {
                 const longitude = this.initialLongitude;
                 const latitude = this.initialLatitude;
                 const zoomLevel = this.initialZoomLevel;
                 
                 // 计算13级地图对应的相机高度
                 const height = this.calculateHeightForZoomLevel(zoomLevel);
                 
                 this.viewer.camera.setView({
                     destination: Cesium.Cartesian3.fromDegrees(longitude, latitude, height),
                     orientation: { 
                         heading: 0.0, 
                         pitch: -Cesium.Math.PI_OVER_TWO + 0.1, 
                         roll: 0.0 
                     }
                 });
                 
                 // console.log(`地图已定位到: ${longitude}, ${latitude}, 缩放级别: ${zoomLevel}`);
                 this.status = `地图已定位到指定坐标 (${longitude.toFixed(6)}, ${latitude.toFixed(6)})，缩放级别: ${zoomLevel}`;
                 
                 // 更新当前缩放级别
                 this.currentZoomLevel = zoomLevel;
                 
                 // 标记初始定位已完成
                 this.initialLocationSet = true;
             } catch (e) {
                 console.warn('定位到初始位置失败:', e);
                 this.status = '定位到初始位置失败';
             }
         },
         
         // 根据缩放级别计算相机高度
         calculateHeightForZoomLevel(zoomLevel) {
             // 13级地图对应的相机高度大约为 8000 米
             // 使用更精确的计算方法
             const earthRadius = 6371000; // 地球半径（米）
             const tileSize = 256; // 瓦片大小
             const pixelsPerTile = tileSize * Math.pow(2, zoomLevel);
             const metersPerPixel = (2 * Math.PI * earthRadius) / pixelsPerTile;
             const height = metersPerPixel * 1000; // 1000像素对应的高度
             
             return Math.max(height, 5000); // 最小高度5000米
         },
         
         // 更新当前缩放级别
         updateCurrentZoomLevel() {
             if (!this.viewer || !this.initialLocationSet) return;
             
             try {
                 const camera = this.viewer.camera;
                 const height = camera.positionCartographic.height;
                 
                 // 简化的缩放级别计算方法
                 // 13级对应约8000米高度，每级高度减半
                 const baseHeight = 8000;
                 const baseZoom = 13;
                 const zoomLevel = baseZoom - Math.log2(height / baseHeight);
                 
                 const newZoomLevel = Math.max(0, Math.round(zoomLevel));
                 
                 // 只有当缩放级别真正改变时才更新
                 if (newZoomLevel !== this.currentZoomLevel) {
                     this.currentZoomLevel = newZoomLevel;
                 }
             } catch (e) {
                 console.warn('更新缩放级别失败:', e);
             }
         },
         
         // 重置到初始位置
         resetToInitialLocation() {
             // 重置初始定位标记，允许重新定位
             this.initialLocationSet = false;
             this.focusToInitialLocation();
         },
         
         // 手动更新缩放级别
         forceUpdateZoomLevel() {
             this.updateCurrentZoomLevel();
         },
        
        // ================= WebSocket相关 =================
        connectWebSocket() {
            try {
                this.websocket = new WebSocket(this.websocketUrl);
                
                                                 this.websocket.onopen = () => {
                    this.isConnected = true;
                    this.reconnectAttempts = 0;
                    this.status = 'WebSocket已连接，正在获取任务信息...';
                    // console.log('WebSocket连接成功');
                    
                    // 连接成功后首先获取任务信息
                    this.sendTaskMessage();
                };
                
                this.websocket.onmessage = (event) => {
                    this.handleWebSocketMessage(event.data);
                };
                
                this.websocket.onclose = () => {
                    this.isConnected = false;
                    this.status = 'WebSocket连接断开，尝试重连...';
                                         // console.log('WebSocket连接断开');
                    this.scheduleReconnect();
                };
                
                this.websocket.onerror = (error) => {
                    console.error('WebSocket错误:', error);
                    this.status = 'WebSocket连接错误';
                };
                
            } catch (error) {
                console.error('WebSocket连接失败:', error);
                this.status = 'WebSocket连接失败';
                this.scheduleReconnect();
            }
        },
        
        disconnectWebSocket() {
            if (this.websocket) {
                this.websocket.close();
                this.websocket = null;
            }
            if (this.reconnectInterval) {
                clearInterval(this.reconnectInterval);
                this.reconnectInterval = null;
            }
        },
        
        reconnectWebSocket() {
            this.disconnectWebSocket();
            this.reconnectAttempts = 0;
            this.connectWebSocket();
        },
        
                 scheduleReconnect() {
             if (this.reconnectAttempts >= this.maxReconnectAttempts) {
                 this.status = 'WebSocket重连失败，请检查服务器状态';
                 return;
             }
             
             this.reconnectAttempts++;
             const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 10000); // 指数退避
             
             this.reconnectInterval = setTimeout(() => {
                 this.status = `尝试重连WebSocket (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`;
                 this.connectWebSocket();
             }, delay);
         },
         
         // 获取需求对应的航线
         sendInitMessage() {
             if (!this.isConnected || !this.websocket) {
                 console.warn('WebSocket未连接，无法获取航线');
                 return;
             }
             
                           try {
                  const initMessage = {
                      wsClientType: this.wsClientType,
                      planId: parseInt(this.planId) || 1,
                      schemaRequirementId: parseInt(this.schemaRequirementId) || 815
                  };
                  this.websocket.send(JSON.stringify(initMessage));
                  // console.log('已发送获取航线请求:', initMessage);
                  this.status = '已发送获取需求对应航线的请求...';
              } catch (error) {
                  console.error('获取航线失败:', error);
                  this.status = '获取航线失败';
              }
         },
         
         // 发送起降场信息请求
         sendLandingFieldMessage() {
             if (!this.isConnected || !this.websocket) {
                 console.warn('WebSocket未连接，无法发送起降场信息请求');
                 return;
             }
             
             try {
                 const landingFieldMessage = {
                     wsClientType: 4,
                     schemaRequirementId: parseInt(this.schemaRequirementId) || 815
                 };
                 this.websocket.send(JSON.stringify(landingFieldMessage));
                 // console.log('已发送起降场信息请求:', landingFieldMessage);
                 this.status = '已发送起降场信息请求...';
             } catch (error) {
                 console.error('发送起降场信息请求失败:', error);
                 this.status = '发送起降场信息请求失败';
             }
         },
         
         // 发送获取任务信息请求
         sendTaskMessage() {
             console.log(`=== 发送任务信息请求 ===`);
             console.log(`WebSocket连接状态: ${this.isConnected}`);
             console.log(`WebSocket对象:`, this.websocket);
             
             if (!this.isConnected || !this.websocket) {
                 console.warn('❌ WebSocket未连接，无法发送任务信息请求');
                 return;
             }
             
             try {
                 const taskMessage = {
                     wsClientType: 5,
                     schemaRequirementId: parseInt(this.schemaRequirementId) || 815
                 };
                 
                 console.log(`📤 发送任务信息请求:`, taskMessage);
                 this.websocket.send(JSON.stringify(taskMessage));
                 console.log(`✅ 任务信息请求已发送`);
                 this.status = '已发送任务信息请求...';
             } catch (error) {
                 console.error('❌ 发送任务信息请求失败:', error);
                 this.status = '发送任务信息请求失败';
             }
         },
         
         // 发送仿真验证消息
         sendSimulationMessage() {
             if (!this.isConnected || !this.websocket) {
                 console.warn('WebSocket未连接，无法发送仿真验证消息');
                 return;
             }
             
             try {
                 const simulationMessage = {
                     wsClientType: 6,
                     vid: parseInt(this.vid) || 684,
                     vidStatus: parseInt(this.vidStatus) || 2
                 };
                 this.websocket.send(JSON.stringify(simulationMessage));
                 // console.log('已发送仿真验证消息:', simulationMessage);
                 this.status = '已发送仿真验证消息...';
                 
                 // 清空所有实时轨迹和航线
                 this.clearAllTracksWithoutConfirm();
                 this.clearAllRoutesWithoutConfirm();
                 
                 // 3秒后清空方案ID
                 setTimeout(() => {
                     this.clearPlanId();
                 }, 3000);
                 
             } catch (error) {
                 console.error('发送仿真验证消息失败:', error);
                 this.status = '发送仿真验证消息失败';
             }
         },
        
                 handleWebSocketMessage(data) {
             try {
                 const message = JSON.parse(data);
                 
                 // 处理不同的消息类型
                 if (message.msgType === 1 && message.data && Array.isArray(message.data)) {
                     // msgType = 1: 实时轨迹数据
                     this.processTrackData(message.data);
                 } else if (message.msgType === 3 && message.data) {
                     // msgType = 3: 航线信息
                     this.processRouteData(message.data);
                 } else if (message.msgType === 4 && message.data) {
                     // msgType = 4: 起降场信息
                     this.processLandingFieldData(message.data);
                 } else if (message.msgType === 5 && message.data) {
                     // msgType = 5: 任务列表信息
                     this.processTaskListData(message.data);
                 } else if (message.code === 200 && message.data && message.data.list) {
                     // 兼容旧格式
                     this.processTrackData(message.data.list);
                 } else {
                     // console.log('收到WebSocket消息:', message);
                 }
             } catch (error) {
                 console.error('解析WebSocket消息失败:', error);
             }
         },
        
                 processRouteData(routeData) {
             // 处理航线数据
             // console.log('收到航线信息:', routeData);
             
             try {
                 // 根据航线数据格式进行处理
                 if (Array.isArray(routeData)) {
                     // 如果是数组格式，处理多条航线
                     // console.log(`处理${routeData.length}条航线`);
                     routeData.forEach((route, index) => {
                         // console.log(`处理航线${index}:`, route);
                         this.createRouteOnMap(route, index);
                     });
                 } else {
                     // 如果是单个航线对象
                     // console.log('处理单条航线:', routeData);
                     this.createRouteOnMap(routeData, 0);
                 }
                 
                 this.status = `已加载航线信息，共${Array.isArray(routeData) ? routeData.length : 1}条航线`;
             } catch (error) {
                 console.error('处理航线数据失败:', error);
                 this.status = '处理航线数据失败';
             }
         },
         
         // 处理起降场数据
         processLandingFieldData(landingFieldData) {
             // console.log('收到起降场信息:', landingFieldData);
             
             try {
                 if (Array.isArray(landingFieldData)) {
                     // 清除之前的起降场实体
                     this.clearLandingFieldEntities();
                     
                     // 处理每个起降场
                     landingFieldData.forEach((field, index) => {
                         this.createLandingFieldEntity(field, index);
                     });
                     
                     // console.log(`已加载${landingFieldData.length}个起降场`);
                     this.status = `已加载起降场信息，共${landingFieldData.length}个起降场`;
                 } else {
                     console.warn('起降场数据格式错误，应为数组:', landingFieldData);
                     this.status = '起降场数据格式错误';
                 }
             } catch (error) {
                 console.error('处理起降场数据失败:', error);
                 this.status = '处理起降场数据失败';
             }
         },
         
         // 创建起降场实体
         createLandingFieldEntity(field, index) {
             try {
                 // 解析经纬度
                 const locationParts = field.location.split(',');
                 if (locationParts.length !== 2) {
                     console.warn(`起降场${index}坐标格式错误:`, field.location);
                     return;
                 }
                 
                 const longitude = parseFloat(locationParts[0]);
                 const latitude = parseFloat(locationParts[1]);
                 
                 if (isNaN(longitude) || isNaN(latitude)) {
                     console.warn(`起降场${index}坐标解析失败:`, field.location);
                     return;
                 }
                 
                 // 创建起降场实体
                 const entity = this.viewer.entities.add({
                     position: Cesium.Cartesian3.fromDegrees(longitude, latitude),
                     point: {
                         pixelSize: 15,
                         color: Cesium.Color.ORANGE,
                         outlineColor: Cesium.Color.WHITE,
                         outlineWidth: 2
                     },
                     label: {
                         text: field.siteName,
                         font: '12pt sans-serif',
                         style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                         outlineWidth: 2,
                         verticalOrigin: Cesium.VerticalOrigin.TOP,
                         pixelOffset: new Cesium.Cartesian2(0, 20),
                         fillColor: Cesium.Color.ORANGE,
                         outlineColor: Cesium.Color.WHITE
                     }
                 });
                 
                 // 存储实体引用和原始数据
                 if (!this.landingFieldEntities) {
                     this.landingFieldEntities = {};
                 }
                 this.landingFieldEntities[field.id] = {
                     entity: entity,
                     data: field
                 };
                 
                 // console.log(`起降场 ${field.siteName} (ID: ${field.id}) 已添加到地图`);
                 
                 // console.log(`已创建起降场${index + 1}: ${field.siteName} (${longitude}, ${latitude})`);
                 
             } catch (error) {
                 console.error(`创建起降场实体失败:`, error);
             }
         },
         
         // 显示起降场详情对话框
         showLandingFieldDialog(fieldData) {
             try {
                 // 解析extraAttributes
                 let extraAttributes = {};
                 if (fieldData.extraAttributes) {
                     try {
                         extraAttributes = JSON.parse(fieldData.extraAttributes);
                     } catch (e) {
                         console.warn('解析extraAttributes失败:', e);
                         extraAttributes = { '原始数据': fieldData.extraAttributes };
                     }
                 }
                 
                 // 显示对话框
                 this.showEditDialog(fieldData.siteName, extraAttributes, fieldData);
                 
             } catch (error) {
                 console.error('显示起降场对话框失败:', error);
             }
         },
         
         // 清除起降场实体
         clearLandingFieldEntities() {
             if (this.landingFieldEntities) {
                 Object.values(this.landingFieldEntities).forEach(item => {
                     this.viewer.entities.remove(item.entity);
                 });
                 this.landingFieldEntities = {};
             }
         },
         
         // 添加全局点击事件处理器
         addGlobalClickHandler() {
             if (!this.viewer) return;
             
             // 创建全局点击事件处理器
             this.globalClickHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
             
             this.globalClickHandler.setInputAction((click) => {
                 // console.log('点击事件触发');
                 const pickedObject = this.viewer.scene.pick(click.position);
                 // console.log('点击的对象:', pickedObject);
                 
                 if (pickedObject && pickedObject.id) {
                     // console.log('点击的实体ID:', pickedObject.id);
                     // 检查是否是起降场实体
                     if (this.landingFieldEntities) {
                         // console.log('当前起降场实体数量:', Object.keys(this.landingFieldEntities).length);
                         Object.values(this.landingFieldEntities).forEach(item => {
                             // console.log('检查实体:', item.entity, '是否匹配:', item.entity === pickedObject.id);
                             if (item.entity === pickedObject.id) {
                                 // console.log('点击了起降场:', item.data.siteName);
                                 this.showLandingFieldDialog(item.data);
                                 return;
                             }
                         });
                     }
                 }
             }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
             
             // console.log('已添加全局点击事件处理器');
         },
         
         // 显示编辑对话框
         showEditDialog(title, extraAttributes, originalData) {
             this.currentEditTitle = title;
             this.currentEditData = originalData;
             this.editedExtraAttributes = JSON.parse(JSON.stringify(extraAttributes)); // 深拷贝
             this.showEditDialogFlag = true;
         },
         
         // 关闭编辑对话框
         closeEditDialog() {
             this.showEditDialogFlag = false;
             this.currentEditData = null;
             this.currentEditTitle = '';
             this.editedExtraAttributes = {};
         },
         
         // 保存编辑对话框
         saveEditDialog() {
             try {
                 // 更新原始数据
                 if (this.currentEditData) {
                     this.currentEditData.extraAttributes = JSON.stringify(this.editedExtraAttributes);
                     // console.log('已更新起降场数据:', this.currentEditData);
                     
                     // 这里可以添加向服务器发送更新请求的逻辑
                     // this.updateLandingFieldData(this.currentEditData);
                 }
                 
                 this.closeEditDialog();
                 this.status = '起降场数据已更新';
                 
             } catch (error) {
                 console.error('保存编辑数据失败:', error);
                 this.status = '保存编辑数据失败';
             }
         },
         
         // 将飞机实体与任务关联
         associateAircraftWithTask(trackId, aircraftEntity) {
             // 查找对应的任务
             const task = this.taskList.find(task => task.uuid === trackId);
             if (task && this.taskStatus[task.uuid]) {
                 this.taskStatus[task.uuid].aircraftEntity = aircraftEntity;
                 // console.log(`已将飞机实体与任务 ${task.uuid} 关联`);
             }
         },
         
         // 检查任务是否完成
         checkTaskCompletion(trackId, currentPoint) {
             // 查找对应的任务
             const task = this.taskList.find(task => task.uuid === trackId);
             if (!task || !this.taskStatus[task.uuid]) return;
             
             const taskStatus = this.taskStatus[task.uuid];
             if (taskStatus.completed) return; // 任务已完成
             
             // 检查是否到达终点起降场
             if (task.destinationId && this.landingFieldEntities[task.destinationId]) {
                 const destinationField = this.landingFieldEntities[task.destinationId].data;
                 const destinationLocation = destinationField.location.split(',');
                 const destLng = parseFloat(destinationLocation[0]);
                 const destLat = parseFloat(destinationLocation[1]);
                 
                 // 计算距离（简单欧几里得距离，实际应用中可能需要更精确的计算）
                 const distance = Math.sqrt(
                     Math.pow(currentPoint.longitude - destLng, 2) + 
                     Math.pow(currentPoint.latitude - destLat, 2)
                 );
                 
                 // 如果距离小于阈值（约100米），认为到达终点
                 const threshold = 0.001; // 约100米
                 if (distance < threshold) {
                     this.completeTask(task.uuid);
                 }
             }
         },
         
         // 完成任务
         completeTask(taskUuid) {
             const taskStatus = this.taskStatus[taskUuid];
             if (!taskStatus || taskStatus.completed) return;
             
             taskStatus.completed = true;
             
             // 隐藏飞机实体
             if (taskStatus.aircraftEntity) {
                 taskStatus.aircraftEntity.show = false;
                 // console.log(`任务 ${taskUuid} 完成，已隐藏飞机图标`);
             }
             
             // 更新状态
             const task = this.taskList.find(t => t.uuid === taskUuid);
             if (task) {
                 this.status = `任务 "${task.name}" 已完成，飞机图标已隐藏`;
             }
         },
         
         // 处理任务列表数据
         processTaskListData(taskData) {
             console.log(`=== 处理任务列表数据 ===`);
             console.log('📥 收到任务列表信息:', taskData);
             console.log('任务数据类型:', typeof taskData);
             console.log('任务数据是否为数组:', Array.isArray(taskData));
             
             try {
                 if (Array.isArray(taskData)) {
                     console.log(`📋 更新任务列表，任务数量: ${taskData.length}`);
                     this.taskList = taskData;
                     
                     // 初始化任务状态跟踪
                     taskData.forEach((task, index) => {
                         console.log(`任务${index + 1}:`, {
                             uuid: task.uuid,
                             name: task.name,
                             aircraftType: task.aircraftType
                         });
                         
                         if (task.uuid && !this.taskStatus[task.uuid]) {
                             this.taskStatus[task.uuid] = {
                                 completed: false,
                                 aircraftEntity: null,
                                 originId: task.originId,
                                 destinationId: task.destinationId
                             };
                         }
                     });
                     
                     console.log(`🔄 开始更新所有飞行器标签...`);
                     // 更新所有飞行器的标签文本
                     this.updateAllAircraftLabels();
                     
                     // 重置请求状态
                     this.isRequestingTasks = false;
                     console.log(`✅ 重置请求状态: ${this.isRequestingTasks}`);
                     
                     console.log(`✅ 已加载${taskData.length}个任务`);
                     console.log('📋 任务列表uuid:', taskData.map(t => t.uuid));
                     this.status = `已加载任务列表，共${taskData.length}个任务`;
                 } else {
                     console.warn('任务数据格式错误，应为数组:', taskData);
                     this.status = '任务数据格式错误';
                 }
             } catch (error) {
                 console.error('处理任务列表数据失败:', error);
                 this.status = '处理任务列表数据失败';
             }
         },
         
         createRouteOnMap(route, index) {
             // 在地图上创建航线
             try {
                 // console.log(`开始创建航线${index}:`, route);
                 
                 // 根据航线数据格式创建航线实体
                 // 航线数据格式和轨迹数据类似，包含pts数组
                 if (route.pts && Array.isArray(route.pts)) {
                     // console.log(`航线${index}使用pts格式，共${route.pts.length}个点`);
                     // 创建航线路径
                     const positions = [];
                     route.pts.forEach((point, pointIndex) => {
                         if (point.lng && point.lat) {
                             positions.push(Cesium.Cartesian3.fromDegrees(point.lng, point.lat, point.alt || 100));
                             // console.log(`航线${index}点${pointIndex}: ${point.lng}, ${point.lat}, ${point.alt || 100}`);
                         } else {
                             console.warn(`航线${index}点${pointIndex}坐标无效:`, point);
                         }
                     });
                     
                     // console.log(`航线${index}有效位置数量:`, positions.length);
                     
                     if (positions.length >= 2) {
                         // 创建航线实体
                         const routeEntity = this.viewer.entities.add({
                             polyline: {
                                 positions: positions,
                                 width: 6,
                                 material: Cesium.Color.BLUE.withAlpha(1.0), // 使用蓝色区分航线
                                 clampToGround: false
                             }
                         });
                         
                         // 存储航线实体引用
                         if (!this.routeEntities) {
                             this.routeEntities = {};
                         }
                         this.routeEntities[`route_${index}`] = routeEntity;
                         
                         // console.log(`已创建航线${index + 1}: ${route.aircraftType || '航线'}${index + 1} (${route.missionType || ''})`);
                         // console.log('航线实体:', routeEntity);
                         
                         // 检查实体是否成功添加到viewer
                         // console.log('当前viewer实体数量:', this.viewer.entities.values.length);
                     }
                 } else if (route.points && Array.isArray(route.points)) {
                     // 兼容旧格式
                     // console.log(`航线${index}使用points格式，共${route.points.length}个点`);
                     const positions = [];
                     route.points.forEach((point, pointIndex) => {
                         if (point.lng && point.lat) {
                             positions.push(Cesium.Cartesian3.fromDegrees(point.lng, point.lat, point.alt || 100));
                         }
                     });
                     
                     if (positions.length >= 2) {
                         const routeEntity = this.viewer.entities.add({
                             polyline: {
                                 positions: positions,
                                 width: 6,
                                 material: Cesium.Color.BLUE.withAlpha(1.0),
                                 clampToGround: false
                             }
                         });
                         
                         if (!this.routeEntities) {
                             this.routeEntities = {};
                         }
                         this.routeEntities[`route_${index}`] = routeEntity;
                         
                         // console.log(`已创建航线${index + 1}:`, route.name || `航线${index + 1}`);
                     }
                 } else if (route.coordinates && Array.isArray(route.coordinates)) {
                     // 兼容GeoJSON格式
                     // console.log(`航线${index}使用coordinates格式，共${route.coordinates.length}个坐标`);
                     const positions = [];
                     route.coordinates.forEach((coord, coordIndex) => {
                         if (coord.length >= 2) {
                             positions.push(Cesium.Cartesian3.fromDegrees(coord[0], coord[1], coord[2] || 100));
                         }
                     });
                     
                     if (positions.length >= 2) {
                         const routeEntity = this.viewer.entities.add({
                             polyline: {
                                 positions: positions,
                                 width: 6,
                                 material: Cesium.Color.BLUE.withAlpha(1.0),
                                 clampToGround: false
                             }
                         });
                         
                         if (!this.routeEntities) {
                             this.routeEntities = {};
                         }
                         this.routeEntities[`route_${index}`] = routeEntity;
                         
                         // console.log(`已创建航线${index + 1}:`, route.name || `航线${index + 1}`);
                     }
                 }
             } catch (error) {
                 console.error('创建航线失败:', error);
             }
         },
         
         processTrackData(trackList) {
             // 处理轨迹数据
             
             // 如果是第一次收到轨迹数据，获取第一个轨迹的pid作为方案ID
             if (this.flightTracks.length === 0 && trackList.length > 0 && trackList[0].pid) {
                 const firstTrackPid = trackList[0].pid;
                 this.planId = firstTrackPid.toString();
                 this.batchPlanId = firstTrackPid;
                 console.log(`从第一条轨迹获取方案ID: ${firstTrackPid}`);
                 this.status = `已从轨迹数据获取方案ID: ${firstTrackPid}`;
                 
                 // 自动重新获取航线
                 this.autoGetRoutes();
             }
             
             trackList.forEach((trackData, index) => {
                const trackId = trackData.tid;
                const color = this.trackColors[index % this.trackColors.length];
                
                // 转换数据格式 - 兼容新旧格式，并过滤无效数据
                const points = trackData.pts
                    .filter(pt => pt && 
                        typeof pt.lng === 'number' && !isNaN(pt.lng) &&
                        typeof pt.lat === 'number' && !isNaN(pt.lat) &&
                        typeof pt.alt === 'number' && !isNaN(pt.alt) &&
                        pt.lng >= -180 && pt.lng <= 180 &&
                        pt.lat >= -90 && pt.lat <= 90)
                    .map((pt, ptIndex) => ({
                        longitude: pt.lng,
                        latitude: pt.lat,
                        altitude: pt.alt,
                        speed: pt.spd,
                        timestamp: pt.ts - trackData.startTime, // 相对时间戳
                        name: `轨迹${index + 1}点${ptIndex}`,
                        // 新增字段
                        battery: pt.batt,
                        timestamp_absolute: pt.ts
                    }));
                
                // 如果过滤后没有有效点，跳过这条轨迹
                if (points.length === 0) {
                    console.warn(`轨迹 ${trackId} 没有有效的坐标点，已跳过`);
                    return;
                }
                
                // 更新或创建轨迹
                const trackDataToUpdate = {
                    id: trackId,
                    color: color,
                    points: points,
                    startTime: trackData.startTime,
                    endTime: trackData.endTime,
                    duration: trackData.dur,
                    distance: trackData.dist,
                    // 新增字段
                    aircraft_type: trackData.aircraft_type,
                    mission_type: trackData.mission_type,
                    esId: trackData.esId,
                    pid: trackData.pid,
                    tid: trackData.tid, // 添加tid字段
                    relId: trackData.relId,
                    schema_requirement_id: trackData.schema_requirement_id,
                    sid: trackData.sid,
                    spId: trackData.spId,
                    typeId: trackData.typeId,
                    vid: trackData.vid
                };
                
                this.updateTrack(trackId, trackDataToUpdate);
            });
            
            const aircraftTypes = [...new Set(trackList.map(t => t.aircraft_type).filter(Boolean))];
            const typeInfo = aircraftTypes.length > 0 ? ` (${aircraftTypes.join(', ')})` : '';
            const totalPoints = this.flightTracks.reduce((sum, track) => 
                sum + (track.points ? track.points.length : 0), 0
            );
            this.status = `已接收${trackList.length}条轨迹数据${typeInfo}，累计${totalPoints}个轨迹点，实时渲染中...`;
            
            // 如果是首次加载轨迹数据，定位到第一条轨迹的第一个点，并选中第一个轨迹
            if (this.flightTracks.length === trackList.length && trackList.length > 0) {
                // 延迟一点时间确保轨迹实体已创建完成
                setTimeout(() => {
                    this.focusToFirstTrackPoint();
                    // 自动选中第一个轨迹
                    this.setCurrentTrack(0);
                }, 500);
            }
        },
        
        updateTrack(trackId, trackData) {
            // 如果轨迹不存在，创建新的
            if (!this.flightTracks.find(t => t.id === trackId)) {
                this.flightTracks.push(trackData);
                
                // 创建轨迹线
                this.createTrackPolyline(trackId, trackData);
                
                // 创建飞机实体
                this.createAircraftEntity(trackId, trackData);
                
                // 如果是第一条轨迹，设置相机位置并选中
                if (this.flightTracks.length === 1) {
                    this.focusFirstPoint();
                    // 自动选中第一个轨迹
                    this.setCurrentTrack(0);
                }
            } else {
                // 更新现有轨迹 - 累积轨迹点而不是替换
                const trackIndex = this.flightTracks.findIndex(t => t.id === trackId);
                const existingTrack = this.flightTracks[trackIndex];
                
                // 合并轨迹点，避免重复
                const existingPoints = existingTrack.points || [];
                const newPoints = trackData.points || [];
                
                // 创建时间戳映射，用于去重
                const existingTimestamps = new Set(existingPoints.map(p => p.timestamp_absolute));
                
                // 添加新的轨迹点（不重复的）
                const uniqueNewPoints = newPoints.filter(point => 
                    !existingTimestamps.has(point.timestamp_absolute)
                );
                
                if (uniqueNewPoints.length > 0) {
                    // 合并轨迹点并按时间排序
                    const allPoints = [...existingPoints, ...uniqueNewPoints].sort((a, b) => 
                        a.timestamp_absolute - b.timestamp_absolute
                    );
                    
                    // 更新轨迹数据
                    const updatedTrackData = {
                        ...trackData,
                        points: allPoints
                    };
                    
                    this.flightTracks[trackIndex] = updatedTrackData;
                    
                    // 更新轨迹线
                    this.updateTrackPolyline(trackId, updatedTrackData);
                    
                    // 更新飞机位置
                    this.updateAircraftPosition(trackId, updatedTrackData);
                    
                                         // 更新轨迹属性面板
                     if (this.selectedTrackId === trackId) {
                         this.selectedTrackData = updatedTrackData;
                         if (allPoints.length > 0) {
                             // 更新最近10个轨迹点
                             this.recentTrackPoints = allPoints.slice(-10).reverse();
                             this.currentTrackPoint = allPoints[allPoints.length - 1];
                             // 更新倒数第二个轨迹点
                             if (allPoints.length >= 2) {
                                 this.previousTrackPoint = allPoints[allPoints.length - 2];
                             }
                             // 更新倒数第三个轨迹点
                             if (allPoints.length >= 3) {
                                 this.thirdLastTrackPoint = allPoints[allPoints.length - 3];
                             }
                             // 更新倒数第四个轨迹点
                             if (allPoints.length >= 4) {
                                 this.fourthLastTrackPoint = allPoints[allPoints.length - 4];
                             }
                         }
                     }
                     
                     // 更新第二个轨迹数据（如果存在）
                     if (this.flightTracks.length >= 2) {
                         this.secondTrackData = this.flightTracks[1];
                     }
                    
                    // console.log(`轨迹 ${trackId} 新增 ${uniqueNewPoints.length} 个轨迹点，总计 ${allPoints.length} 个点`);
                }
            }
        },
        
        // ================= 实时渲染相关 =================
        initRealTimeRendering() {
            // 启动实时渲染定时器（如果启用实时更新）
            if (this.enableRealTimeUpdate) {
                this.renderInterval = setInterval(() => {
                    this.renderNewPoints();
                }, this.renderDelay);
            }
        },
        
                 renderNewPoints() {
             const currentTime = Date.now();
             
             // 节流控制，避免过于频繁的更新
             if (currentTime - this.lastUpdateTime < this.updateThrottle) {
                 return;
             }
             
             // 批量更新所有轨迹和飞机位置，减少渲染次数
            this.flightTracks.forEach(track => {
                 if (track.points && track.points.length > 0) {
                     // 更新轨迹线（包括颜色）
                     this.updateTrackPolylinePositions(track.id, track);
                     
                     // 更新飞机位置
                     this.updateAircraftPosition(track.id, track);
                 }
             });
             
             this.lastRenderTime = currentTime;
             this.lastUpdateTime = currentTime;
         },
        
                 createTrackPolyline(trackId, trackData) {
             // 创建轨迹线实体 - 使用静态位置数组，避免频繁的CallbackProperty调用
             const polylineEntity = this.viewer.entities.add({
                 polyline: {
                     positions: [], // 初始为空数组
                     width: 3,
                     material: trackData.color.withAlpha(0.8),
                     clampToGround: false,
                     show: this.showHistoricalTracks // 根据设置控制可见性
                 }
             });
             
             // 存储实体引用，用于后续更新
             this.trackPolylines[trackId] = polylineEntity;
             
             // 立即更新轨迹线
             this.updateTrackPolylinePositions(trackId, trackData);
             
             // 隐藏起点标签 - 不显示任何起点信息
             // if (trackData.points.length > 0) {
             //     const startPoint = trackData.points[0];
             //     
             //     // 验证起点坐标的有效性
             //     if (startPoint && 
             //         typeof startPoint.longitude === 'number' && !isNaN(startPoint.longitude) &&
             //         typeof startPoint.latitude === 'number' && !isNaN(startPoint.latitude) &&
             //         typeof startPoint.altitude === 'number' && !isNaN(startPoint.altitude) &&
             //         startPoint.longitude >= -180 && startPoint.longitude <= 180 &&
             //         startPoint.latitude >= -90 && startPoint.latitude <= 90) {
             //         
             //         const labelText = trackData.aircraft_type ? 
             //             `${trackData.aircraft_type}\n${trackData.mission_type || '轨迹'}` : 
             //             `轨迹${this.flightTracks.length}`;
             //         
             //         try {
             //             this.viewer.entities.add({
             //                 position: Cesium.Cartesian3.fromDegrees(startPoint.longitude, startPoint.latitude, startPoint.altitude),
             //                 label: {
             //                     text: labelText,
             //                     font: '12pt sans-serif',
             //                     style: Cesium.LabelStyle.FILL_AND_OUTLINE,
             //                     outlineWidth: 2,
             //                     verticalOrigin: Cesium.VerticalOrigin.TOP,
             //                     pixelOffset: new Cesium.Cartesian2(0, 10),
             //                     fillColor: trackData.color,
             //                     outlineColor: Cesium.Color.BLACK
             //                 }
             //             });
             //         } catch (e) {
             //             console.warn('创建起点标签失败:', startPoint, e);
             //         }
             //     }
             // }
         },
        
        updateTrackPolyline(trackId, trackData) {
            // 更新轨迹线位置
            this.updateTrackPolylinePositions(trackId, trackData);
        },
        
                 updateTrackPolylinePositions(trackId, trackData) {
             const polylineEntity = this.trackPolylines[trackId];
             if (!polylineEntity) return;
             
             const positions = [];
             const currentTime = Date.now();
             
             trackData.points.forEach(point => {
                 // 验证坐标数据的有效性
                 if (point && 
                     typeof point.longitude === 'number' && !isNaN(point.longitude) &&
                     typeof point.latitude === 'number' && !isNaN(point.latitude) &&
                     typeof point.altitude === 'number' && !isNaN(point.altitude) &&
                     point.longitude >= -180 && point.longitude <= 180 &&
                     point.latitude >= -90 && point.latitude <= 90) {
                     try {
                         positions.push(Cesium.Cartesian3.fromDegrees(point.longitude, point.latitude, point.altitude));
                     } catch (e) {
                         console.warn('无效的坐标点:', point, e);
                     }
                 }
             });
             
             // 批量更新位置，减少渲染次数
             if (positions.length >= 2) {
                 polylineEntity.polyline.positions = positions;
                 
                 // 更新轨迹线颜色：飞过的轨迹用青色，未来的轨迹用原色
                 this.updateTrackPolylineColor(trackId, trackData, currentTime);
             }
         },
         
         updateTrackPolylineColor(trackId, trackData, currentTime) {
             const polylineEntity = this.trackPolylines[trackId];
             if (!polylineEntity || !trackData.points || trackData.points.length === 0) return;
             
             // 如果轨迹还没有开始，全部用原色
             if (currentTime < trackData.startTime) {
                 polylineEntity.polyline.material = trackData.color.withAlpha(0.8);
                 return;
             }
             
             // 如果轨迹已经结束，全部用带箭头的青色线条
             const lastPointTime = trackData.startTime + trackData.points[trackData.points.length - 1].timestamp * 1000;
             if (currentTime >= lastPointTime) {
                 // 检查当前材质是否已经是箭头材质
                 if (!polylineEntity.polyline.material || 
                     polylineEntity.polyline.material !== this.arrowLineMaterial) {
                     // 创建带箭头的线条材质
                     const arrowMaterial = this.createArrowLineMaterial();
                     polylineEntity.polyline.material = arrowMaterial;
                     polylineEntity.polyline.width = 4;
                 }
                 return;
             }
             
             // 找到当前时间对应的轨迹点索引
             let currentPointIndex = 0;
             for (let i = 0; i < trackData.points.length; i++) {
                 const pointTime = trackData.startTime + trackData.points[i].timestamp * 1000;
                 if (currentTime >= pointTime) {
                     currentPointIndex = i;
                 } else {
                     break;
                 }
             }
             
             // 计算已飞过的轨迹比例
             const flownRatio = currentPointIndex / (trackData.points.length - 1);
             
             // 如果大部分轨迹已经飞过（超过50%），用带箭头的青色线条；否则用原色
             if (flownRatio > 0.5) {
                 // 检查当前材质是否已经是箭头材质
                 if (!polylineEntity.polyline.material || 
                     polylineEntity.polyline.material !== this.arrowLineMaterial) {
                     // 创建带箭头的线条材质
                     const arrowMaterial = this.createArrowLineMaterial();
                     polylineEntity.polyline.material = arrowMaterial;
                     polylineEntity.polyline.width = 4;
                 }
             } else {
                 // 检查当前材质是否已经是原色材质
                 const originalColor = trackData.color.withAlpha(0.8);
                 if (!polylineEntity.polyline.material || 
                     polylineEntity.polyline.material !== originalColor) {
                     polylineEntity.polyline.material = originalColor;
                     polylineEntity.polyline.width = 3;
                 }
             }
         },
         

         
         // 创建带箭头的线条材质（使用缓存）
         createArrowLineMaterial() {
             // 如果已经有缓存的材质，直接返回
             if (!this.arrowLineMaterial) {
                 // 创建Canvas绘制带箭头的线条纹理
                 const canvas = document.createElement('canvas');
                 canvas.width = 64;
                 canvas.height = 16;
                 const ctx = canvas.getContext('2d');
                 
                 // 清空画布
                 ctx.clearRect(0, 0, 64, 16);
                 
                 // 绘制线条背景（青色）
                 ctx.fillStyle = '#00FFFF';
                 ctx.fillRect(0, 6, 64, 4);
                 
                 // 绘制箭头（白色三角形）
                 ctx.fillStyle = '#FFFFFF';
                 ctx.strokeStyle = '#00FFFF';
                 ctx.lineWidth = 1;
                 
                 // 在线条中间绘制箭头
                 ctx.beginPath();
                 ctx.moveTo(48, 8);  // 箭头尖端
                 ctx.lineTo(40, 4);  // 左翼
                 ctx.lineTo(40, 12); // 右翼
                 ctx.closePath();
                 
                 // 填充箭头
                 ctx.fill();
                 ctx.stroke();
                 
                 // 创建重复纹理并缓存
                 this.arrowLineMaterial = new Cesium.ImageMaterialProperty({
                     image: canvas,
                     repeat: new Cesium.Cartesian2(1, 1)
                 });
             }
             
             return this.arrowLineMaterial;
         },
         

         
         // 创建分段颜色的轨迹线（更精确的方法）
         createSegmentedTrackPolyline(trackId, trackData, currentTime) {
             // 移除现有的轨迹线
             const existingPolyline = this.trackPolylines[trackId];
             if (existingPolyline) {
                 this.viewer.entities.remove(existingPolyline);
             }
             
             // 找到当前时间对应的轨迹点索引
             let currentPointIndex = 0;
             for (let i = 0; i < trackData.points.length; i++) {
                 const pointTime = trackData.startTime + trackData.points[i].timestamp * 1000;
                 if (currentTime >= pointTime) {
                     currentPointIndex = i;
                 } else {
                     break;
                 }
             }
             
             // 创建飞过的轨迹段（青色）
             if (currentPointIndex > 0) {
                 const flownPositions = [];
                 for (let i = 0; i <= currentPointIndex; i++) {
                     const point = trackData.points[i];
                     if (point && 
                         typeof point.longitude === 'number' && !isNaN(point.longitude) &&
                         typeof point.latitude === 'number' && !isNaN(point.latitude) &&
                         typeof point.altitude === 'number' && !isNaN(point.altitude)) {
                         try {
                             flownPositions.push(Cesium.Cartesian3.fromDegrees(point.longitude, point.latitude, point.altitude));
                         } catch (e) {
                             console.warn('无效的坐标点:', point, e);
                         }
                     }
                 }
                 
                 if (flownPositions.length >= 2) {
                     // 创建带箭头的线条材质
                     const arrowMaterial = this.createArrowLineMaterial();
                     
                     const flownPolyline = this.viewer.entities.add({
                         polyline: {
                             positions: flownPositions,
                             width: 4,
                             material: arrowMaterial,
                             clampToGround: false,
                             show: this.showHistoricalTracks
                         }
                     });
                     
                     // 存储飞过的轨迹段引用
                     this.trackPolylines[trackId + '_flown'] = flownPolyline;
                 }
             }
             
             // 创建未来的轨迹段（原色）
             if (currentPointIndex < trackData.points.length - 1) {
                 const futurePositions = [];
                 for (let i = currentPointIndex; i < trackData.points.length; i++) {
                     const point = trackData.points[i];
                     if (point && 
                         typeof point.longitude === 'number' && !isNaN(point.longitude) &&
                         typeof point.latitude === 'number' && !isNaN(point.latitude) &&
                         typeof point.altitude === 'number' && !isNaN(point.altitude)) {
                         try {
                             futurePositions.push(Cesium.Cartesian3.fromDegrees(point.longitude, point.latitude, point.altitude));
                         } catch (e) {
                             console.warn('无效的坐标点:', point, e);
                         }
                     }
                 }
                 
                 if (futurePositions.length >= 2) {
                     const futurePolyline = this.viewer.entities.add({
                         polyline: {
                             positions: futurePositions,
                             width: 3,
                             material: trackData.color.withAlpha(0.8),
                             clampToGround: false,
                             show: this.showHistoricalTracks
                         }
                     });
                     
                     // 存储未来的轨迹段引用
                     this.trackPolylines[trackId] = futurePolyline;
                 }
             }
         },
        
                          createAircraftEntity(trackId, trackData) {
             // 创建飞机实体（飞行器图标）- 使用静态位置，避免频繁的CallbackProperty调用
             console.log(`创建飞行器实体 ${trackId}, pid: ${trackData.pid}`);
             const aircraftEntity = this.viewer.entities.add({
                 position: Cesium.Cartesian3.fromDegrees(0, 0, 0), // 初始位置
                 billboard: {
                     image: this.createColoredDroneIcon(trackData.color),
                     width: 48,
                     height: 48,
                     eyeOffset: new Cesium.Cartesian3(0, 0, -100),
                     alignedAxis: Cesium.Cartesian3.UNIT_Z // 添加对齐轴
                 },
                 label: { 
                     text: this.getTaskNameByTrackId(trackId),
                     font: '12pt sans-serif', 
                     style: Cesium.LabelStyle.FILL_AND_OUTLINE, 
                     outlineWidth: 2,
                     verticalOrigin: Cesium.VerticalOrigin.BOTTOM, 
                     pixelOffset: new Cesium.Cartesian2(0, -30),
                     fillColor: trackData.color,
                     outlineColor: Cesium.Color.BLACK 
                 }
             });
             
             this.aircraftEntities[trackId] = aircraftEntity;
             
             // 将飞机实体与任务关联
             this.associateAircraftWithTask(trackId, aircraftEntity);
             
             // 立即更新飞机位置
             this.updateAircraftPosition(trackId, trackData);
         },
        
                 updateAircraftPosition(trackId, trackData) {
             const aircraftEntity = this.aircraftEntities[trackId];
             if (!aircraftEntity) return;
             
             const track = this.flightTracks.find(t => t.id === trackId);
             if (!track || !track.points || track.points.length === 0) return;
             
             // 找到当前时间对应的点
             const currentTime = Date.now();
             // 计算相对于轨迹开始时间的当前时间
             const relativeTime = (currentTime - track.startTime) / 1000;
             const currentPoint = this.getCurrentPoint(track, relativeTime);
             
             if (currentPoint && 
                 typeof currentPoint.longitude === 'number' && !isNaN(currentPoint.longitude) &&
                 typeof currentPoint.latitude === 'number' && !isNaN(currentPoint.latitude) &&
                 typeof currentPoint.altitude === 'number' && !isNaN(currentPoint.altitude) &&
                 currentPoint.longitude >= -180 && currentPoint.longitude <= 180 &&
                 currentPoint.latitude >= -90 && currentPoint.latitude <= 90) {
                 try {
                     // 直接设置飞机位置，确保实时更新准确
                     aircraftEntity.position = Cesium.Cartesian3.fromDegrees(
                         currentPoint.longitude, 
                         currentPoint.latitude, 
                         currentPoint.altitude
                     );
                     
                     // 计算飞行方向
                     const heading = this.calculateAircraftHeading(track, currentPoint, relativeTime);
                     if (heading !== null) {
                         // 更新飞机朝向 - 根据实体类型选择不同的旋转方式
                         const headingRadians = Cesium.Math.toRadians(heading);
                         
                         if (this.timeMode === 'cesium') {
                             // 方案B：使用model的orientation属性
                             if (aircraftEntity.model) {
                                 aircraftEntity.orientation = Cesium.Transforms.headingPitchRollQuaternion(
                                     aircraftEntity.position.getValue(this.viewer.clock.currentTime),
                                     new Cesium.HeadingPitchRoll(headingRadians, 0, 0)
                                 );
                             }
                         } else {
                             // 方案A：使用billboard的rotation属性
                             if (aircraftEntity.billboard) {
                                 aircraftEntity.billboard.rotation = headingRadians;
                             }
                         }
                         
                         // 更新飞行方向显示
                         const direction = this.getHeadingDirection(heading);
                         this.headingText = `${heading.toFixed(1)}° (${direction})`;
                     } else {
                         this.headingText = '-';
                     }
                     
                     // 更新标签文本（只显示任务名称）
                     const taskName = this.getTaskNameByTrackId(trackId);
                     if (aircraftEntity.label) {
                         aircraftEntity.label.text = taskName;
                     }
                     
                     // 检查任务是否完成
                     this.checkTaskCompletion(trackId, currentPoint);
                 } catch (e) {
                     console.warn('更新飞机位置失败:', currentPoint, e);
                 }
             }
         },
        
                 getCurrentPoint(track, currentTime) {
             if (!track.points || track.points.length === 0) return null;
             
             // 验证轨迹点数据的有效性
             const validPoints = track.points.filter(point => 
                 point && 
                 typeof point.longitude === 'number' && !isNaN(point.longitude) &&
                 typeof point.latitude === 'number' && !isNaN(point.latitude) &&
                 typeof point.altitude === 'number' && !isNaN(point.altitude) &&
                 point.longitude >= -180 && point.longitude <= 180 &&
                 point.latitude >= -90 && point.latitude <= 90
             );
             
             if (validPoints.length === 0) return null;
             
             // 计算轨迹的总时长（秒）
             const totalTrackDuration = validPoints[validPoints.length - 1].timestamp;
             
             // 如果当前时间超出轨迹时长，返回最后一个点
             if (currentTime >= totalTrackDuration) {
                 return validPoints[validPoints.length - 1];
             }
             
             // 如果当前时间小于0，返回第一个点
             if (currentTime <= 0) {
                 return validPoints[0];
             }
             
             // 找到当前时间对应的点
             for (let i = 0; i < validPoints.length - 1; i++) {
                 const point1 = validPoints[i];
                 const point2 = validPoints[i + 1];
                 const time1 = point1.timestamp;
                 const time2 = point2.timestamp;
                 
                 if (currentTime >= time1 && currentTime <= time2) {
                     // 计算插值比例
                     const ratio = (currentTime - time1) / (time2 - time1);
                     
                     // 线性插值
                     return {
                         longitude: point1.longitude + (point2.longitude - point1.longitude) * ratio,
                         latitude: point1.latitude + (point2.latitude - point1.latitude) * ratio,
                         altitude: point1.altitude + (point2.altitude - point1.altitude) * ratio,
                         battery: point1.battery,
                         speed: point1.speed
                     };
                 }
             }
             
             return validPoints[0];
         },
         
         // 缓动函数，让插值更平滑
         easeInOutQuad(t) {
             return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
         },
        
                 calculateAircraftHeading(track, currentPoint, currentTime) {
             // 计算飞行器的朝向角度
             try {
                 if (!track.points || track.points.length < 2) return null;
                 
                 // 找到当前点在轨迹中的索引
                 let currentIndex = -1;
                 for (let i = 0; i < track.points.length; i++) {
                     const point = track.points[i];
                     const pointTime = point.timestamp; // 直接使用timestamp，不需要转换
                     if (currentTime >= pointTime) {
                         currentIndex = i;
                     } else {
                         break;
                     }
                 }
                 
                 // 如果找不到当前点或已经是最后一个点，使用前一个方向
                 if (currentIndex === -1 || currentIndex === track.points.length - 1) {
                     if (currentIndex === -1) {
                         // 如果还没开始，使用第一个点到第二个点的方向
                         if (track.points.length >= 2) {
                             return this.calculateHeadingBetweenPoints(track.points[0], track.points[1]);
                         }
                     } else {
                         // 如果已经结束，使用最后两个点的方向
                         if (track.points.length >= 2) {
                             return this.calculateHeadingBetweenPoints(
                                 track.points[track.points.length - 2], 
                                 track.points[track.points.length - 1]
                             );
                         }
                     }
                     return null;
                 }
                 
                 // 计算当前点到下一个点的方向
                 const nextPoint = track.points[currentIndex + 1];
                 const heading = this.calculateHeadingBetweenPoints(currentPoint, nextPoint);
                 
                 // 平滑方向变化
                 if (heading !== null && this.lastHeading !== null) {
                     const headingDiff = heading - this.lastHeading;
                     
                     // 处理角度跨越360度边界的情况
                     let adjustedDiff = headingDiff;
                     if (headingDiff > 180) {
                         adjustedDiff = headingDiff - 360;
                     } else if (headingDiff < -180) {
                         adjustedDiff = headingDiff + 360;
                     }
                     
                     // 应用更小的平滑因子，减少跳跃
                     const smoothFactor = 0.1; // 更小的平滑因子，减少方向跳跃
                     const smoothedHeading = this.lastHeading + adjustedDiff * smoothFactor;
                     
                     this.lastHeading = (smoothedHeading + 360) % 360;
                     return this.lastHeading;
                 } else {
                     this.lastHeading = heading;
                     return heading;
                 }
                 
             } catch (error) {
                 console.warn('计算飞行方向失败:', error);
                 return null;
             }
         },
         
         calculateHeadingBetweenPoints(point1, point2) {
             // 计算两点之间的方位角
             try {
                 const lat1 = Cesium.Math.toRadians(point1.latitude);
                 const lon1 = Cesium.Math.toRadians(point1.longitude);
                 const lat2 = Cesium.Math.toRadians(point2.latitude);
                 const lon2 = Cesium.Math.toRadians(point2.longitude);
                 
                 const dLon = lon2 - lon1;
                 const y = Math.sin(dLon) * Math.cos(lat2);
                 const x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
                 
                 let heading = Math.atan2(y, x);
                 
                 // 转换为度数并确保在0-360范围内
                 heading = Cesium.Math.toDegrees(heading);
                 heading = (heading + 360) % 360;
                 
                 // console.log(`方向计算: 从(${point1.longitude.toFixed(6)}, ${point1.latitude.toFixed(6)}) 到 (${point2.longitude.toFixed(6)}, ${point2.latitude.toFixed(6)}) = ${heading.toFixed(1)}°`);
                 
                 return heading;
             } catch (error) {
                 console.warn('计算方位角失败:', error);
                 return null;
             }
         },
         
         getHeadingDirection(heading) {
             // 将角度转换为方向文字描述
             if (heading === null || heading === undefined) return '';
             
             const directions = [
                 '北', '北偏东', '东北', '东偏北', '东', '东偏南', '东南', '南偏东',
                 '南', '南偏西', '西南', '西偏南', '西', '西偏北', '西北', '北偏西'
             ];
             
             const index = Math.round(heading / 22.5) % 16;
             return directions[index];
         },
         
         addPointsToTrack(trackId, newPoints) {
             // 这里可以添加新点到轨迹的逻辑
             // 由于使用了CallbackProperty，轨迹线会自动更新
         },
        
                 clearAllTracks() {
             if (confirm('确定要清除所有轨迹吗？')) {
                 // 停止播放
                 this.stopPlayback();
                 
                 // 清除所有轨迹实体
                 Object.values(this.trackPolylines).forEach(entity => {
                     if (entity) this.viewer.entities.remove(entity);
                 });
                 Object.values(this.aircraftEntities).forEach(entity => {
                     if (entity) this.viewer.entities.remove(entity);
                 });
                 
                 // 清空数据
                 this.trackPolylines = {};
                 this.aircraftEntities = {};
                 this.flightTracks = [];
                 this.currentTrackIndex = 0;
                 this.flightPath = [];
                 
                 // 重置时间轴
                 this.currentTime = 0;
                 this.totalDuration = 0;
                 this.currentTimePercent = 0;
                 
                 // 重置飞行方向
                 this.lastHeading = null;
                 this.headingText = '-';
                 
                 this.status = '已清除所有轨迹';
             }
         },
         
         // 清空所有轨迹（不需要确认）
         clearAllTracksWithoutConfirm() {
             // 停止播放
             this.stopPlayback();
             
             // 清除所有轨迹实体
             Object.values(this.trackPolylines).forEach(entity => {
                 if (entity) this.viewer.entities.remove(entity);
             });
             Object.values(this.aircraftEntities).forEach(entity => {
                 if (entity) this.viewer.entities.remove(entity);
             });
             
             // 清空数据
             this.trackPolylines = {};
             this.aircraftEntities = {};
             this.flightTracks = [];
             this.currentTrackIndex = 0;
             this.flightPath = [];
             
             // 重置时间轴
             this.currentTime = 0;
             this.totalDuration = 0;
             this.currentTimePercent = 0;
             
             // 重置飞行方向
             this.lastHeading = null;
             this.headingText = '-';
             
             this.status = '已清空所有实时轨迹';
             
             // 清空轨迹属性面板数据
             this.selectedTrackId = '';
             this.selectedTrackData = null;
             this.currentTrackPoint = null;
             this.secondTrackData = null;
             this.previousTrackPoint = null;
             this.thirdLastTrackPoint = null;
             this.fourthLastTrackPoint = null;
             this.recentTrackPoints = [];
         },
         
         // 轨迹选择变化处理
         onTrackSelectionChange() {
             if (this.isUpdatingSelection) return; // 防止循环调用
             
             if (this.selectedTrackId) {
                 // 查找选中的轨迹数据
                 this.selectedTrackData = this.flightTracks.find(track => track.id === this.selectedTrackId);
                 
                 // 同步更新左边面板的选择
                 const trackIndex = this.flightTracks.findIndex(track => track.id === this.selectedTrackId);
                 if (trackIndex !== -1) {
                     this.isUpdatingSelection = true;
                     this.currentTrackIndex = trackIndex;
                     this.currentTrack = this.selectedTrackData;
                     this.flightPath = this.selectedTrackData.points;
                     this.initializeTimeline();
                     this.isUpdatingSelection = false;
                 }
                 
                 // 获取第二个轨迹数据（如果存在）
                 if (this.flightTracks.length >= 2) {
                     this.secondTrackData = this.flightTracks[1]; // 第二个轨迹
                 } else {
                     this.secondTrackData = null;
                 }
                 
                 if (this.selectedTrackData && this.selectedTrackData.points && this.selectedTrackData.points.length > 0) {
                     const points = this.selectedTrackData.points;
                     // 获取最近10个轨迹点（倒置顺序，最新在最前面）
                     this.recentTrackPoints = points.slice(-10).reverse();
                     // 保留原有的单个轨迹点数据（用于兼容性）
                     this.currentTrackPoint = points[points.length - 1];
                     if (points.length >= 2) {
                         this.previousTrackPoint = points[points.length - 2];
                     } else {
                         this.previousTrackPoint = null;
                     }
                     if (points.length >= 3) {
                         this.thirdLastTrackPoint = points[points.length - 3];
                     } else {
                         this.thirdLastTrackPoint = null;
                     }
                     if (points.length >= 4) {
                         this.fourthLastTrackPoint = points[points.length - 4];
                     } else {
                         this.fourthLastTrackPoint = null;
                     }
                 } else {
                     this.recentTrackPoints = [];
                     this.currentTrackPoint = null;
                     this.previousTrackPoint = null;
                     this.thirdLastTrackPoint = null;
                     this.fourthLastTrackPoint = null;
                 }
             } else {
                 this.selectedTrackData = null;
                 this.currentTrackPoint = null;
                 this.secondTrackData = null;
                 this.previousTrackPoint = null;
             }
         },
         
         // 更新当前轨迹点数据
         updateCurrentTrackPoint(trackId, pointData) {
             if (this.selectedTrackId === trackId && this.selectedTrackData) {
                 this.currentTrackPoint = pointData;
             }
         },
         
         // 获取当前日期（年月日）
         getCurrentDate() {
             if (this.recentTrackPoints && this.recentTrackPoints.length > 0) {
                 // 从第一条轨迹点获取日期
                 const firstPoint = this.recentTrackPoints[0];
                 if (firstPoint && firstPoint.timestamp_absolute) {
                     const date = new Date(firstPoint.timestamp_absolute * 1000);
                     const year = date.getFullYear();
                     const month = String(date.getMonth() + 1).padStart(2, '0');
                     const day = String(date.getDate()).padStart(2, '0');
                     return `${year}-${month}-${day}`;
                 }
             }
             // 如果没有轨迹点数据，返回当前系统日期
             const now = new Date();
             const year = now.getFullYear();
             const month = String(now.getMonth() + 1).padStart(2, '0');
             const day = String(now.getDate()).padStart(2, '0');
             return `${year}-${month}-${day}`;
         },
         
         // 格式化时间（只显示小时分钟秒）
         formatTimeOnly(timestamp) {
             const date = new Date(timestamp * 1000);
             const hours = String(date.getHours()).padStart(2, '0');
             const minutes = String(date.getMinutes()).padStart(2, '0');
             const seconds = String(date.getSeconds()).padStart(2, '0');
             return `${hours}:${minutes}:${seconds}`;
         },
         
         // 格式化任务时间
         formatTaskTime(timeString) {
             if (!timeString) return 'N/A';
             try {
                 const date = new Date(timeString);
                 if (isNaN(date.getTime())) return 'N/A';
                 const hours = String(date.getHours()).padStart(2, '0');
                 const minutes = String(date.getMinutes()).padStart(2, '0');
                 return `${hours}:${minutes}`;
             } catch (e) {
                 return 'N/A';
             }
         },
         
         // 获取轨迹点在原始数组中的下标
         getPointIndex(point) {
             if (!this.selectedTrackData || !this.selectedTrackData.points) {
                 return 'N/A';
             }
             const index = this.selectedTrackData.points.findIndex(p => 
                 p.timestamp_absolute === point.timestamp_absolute &&
                 p.longitude === point.longitude &&
                 p.latitude === point.latitude
             );
             return index !== -1 ? index + 1 : 'N/A';
         },
         
         // 环境切换处理
         onEnvChange() {
             console.log(`环境已切换到: ${this.currentEnv}`);
             
             // 根据环境更新WebSocket地址和接口地址
             this.updateWebSocketUrl();
             
             // 显示环境信息
             const envNames = {
                 dev: '开发环境',
                 test: '测试环境',
                 prod: '生产环境'
             };
             
             this.status = `环境已切换到: ${envNames[this.currentEnv]} (${this.currentEnv})`;
             
             // 如果当前已连接，建议重新连接
             if (this.isConnected) {
                 this.status = `环境已切换到: ${envNames[this.currentEnv]}，建议重新连接WebSocket`;
             }
         },
         
         // 隐藏Cesium ion警告消息
         hideIonWarning() {
             // 方法1：通过CSS隐藏ion警告
             const style = document.createElement('style');
             style.textContent = `
                 .cesium-viewer-bottom {
                     display: none !important;
                 }
                 .cesium-viewer-cesiumWidgetContainer .cesium-widget-credits {
                     display: none !important;
                 }
             `;
             document.head.appendChild(style);
             
             // 方法2：直接移除ion警告元素
             setTimeout(() => {
                 const ionElements = document.querySelectorAll('.cesium-viewer-bottom, .cesium-widget-credits');
                 ionElements.forEach(el => {
                     if (el.textContent.includes('CESIUM ion') || el.textContent.includes('access token')) {
                         el.style.display = 'none';
                     }
                 });
             }, 1000);
         },
         
         // 根据环境更新WebSocket地址和接口地址
         updateWebSocketUrl() {
             const envConfigs = {
                 dev: {
                     ws: 'ws://127.0.0.1:6088/uam/v2/ws/uam/channel/245',
                     api: 'http://127.0.0.1:8084'
                 },
                 test: {
                     ws: 'ws://211.159.175.30:8011/uam/v2/ws/uam/channel/245',
                     api: 'http://211.159.175.30:8011'
                 },
                 prod: {
                     ws: 'ws://uam-cloud:8011/uam/v2/ws/uam/channel/245',
                     api: 'http://uam-cloud:8011'
                 }
             };
             
             const config = envConfigs[this.currentEnv] || envConfigs.test;
             this.websocketUrl = config.ws;
             this.apiBaseUrl = config.api;
         },
         
         // 获取轨迹总点数
         getTotalTrackPoints() {
             if (!this.selectedTrackData || !this.selectedTrackData.points) {
                 return 0;
             }
             return this.selectedTrackData.points.length;
         },
         
         // 判断任务是否被选中
         isTaskSelected(taskUuid) {
             if (!this.selectedTrackId) return false;
             const selectedTrack = this.flightTracks.find(t => t.id === this.selectedTrackId);
             return selectedTrack && selectedTrack.tid === taskUuid;
         },
         
         // 根据轨迹ID获取任务名称
         getTaskNameByTrackId(trackId) {
             const track = this.flightTracks.find(t => t.id === trackId);
             if (!track) {
                 return '未知任务';
             }
             
             if (!track.tid) {
                 return '未知任务';
             }
             
             // 如果任务列表为空，重新加载任务列表
             if (this.taskList.length === 0) {
                 if (this.isConnected && this.websocket && !this.isRequestingTasks) {
                     this.isRequestingTasks = true;
                     this.sendTaskMessage();
                     
                     // 延迟一段时间后再次尝试查找
                     setTimeout(() => {
                         const task = this.taskList.find(t => t.uuid === track.tid);
                         if (task) {
                             // 更新飞行器标签
                             const aircraftEntity = this.aircraftEntities[trackId];
                             if (aircraftEntity && aircraftEntity.label) {
                                 aircraftEntity.label.text = task.name;
                             }
                         }
                         this.isRequestingTasks = false;
                     }, 2000);
                 }
                 return '未知任务';
             }
             
             // 在任务列表中查找对应的任务（使用tid匹配uuid）
             let task = this.taskList.find(t => t.uuid === track.tid);
             
             if (!task) {
                 // 如果找不到任务，重新获取任务列表
                 if (this.isConnected && this.websocket && !this.isRequestingTasks) {
                     this.isRequestingTasks = true;
                     this.sendTaskMessage();
                     
                     // 延迟一段时间后再次尝试查找
                     setTimeout(() => {
                         task = this.taskList.find(t => t.uuid === track.tid);
                         
                         if (task) {
                             // 更新飞行器标签
                             const aircraftEntity = this.aircraftEntities[trackId];
                             if (aircraftEntity && aircraftEntity.label) {
                                 aircraftEntity.label.text = task.name;
                             }
                         }
                         this.isRequestingTasks = false;
                     }, 2000); // 等待2秒让任务列表更新
                 }
                 
                 return '未知任务';
             }
             
             return task.name;
         },
         
         // 更新所有飞行器的标签文本
         updateAllAircraftLabels() {
             console.log(`=== 更新所有飞行器标签 ===`);
             console.log(`飞行器实体数量: ${Object.keys(this.aircraftEntities).length}`);
             console.log(`飞行器ID列表:`, Object.keys(this.aircraftEntities));
             
             Object.keys(this.aircraftEntities).forEach((trackId, index) => {
                 console.log(`🔄 更新飞行器${index + 1}: ${trackId}`);
                 const aircraftEntity = this.aircraftEntities[trackId];
                 
                 if (aircraftEntity && aircraftEntity.label) {
                     console.log(`✅ 找到飞行器实体和标签`);
                     const taskName = this.getTaskNameByTrackId(trackId);
                     console.log(`📝 获取到任务名称: ${taskName}`);
                     
                     aircraftEntity.label.text = taskName;
                     console.log(`✅ 已更新飞行器 ${trackId} 标签为: ${taskName}`);
                     
                     // 如果任务名称是"未知任务"，记录日志
                     if (taskName === '未知任务') {
                         console.log(`⚠️ 飞行器 ${trackId} 显示未知任务，已触发重新获取任务列表逻辑`);
                     }
                 } else {
                     console.warn(`❌ 飞行器 ${trackId} 实体或标签不存在`);
                 }
             });
             
             console.log(`=== 更新所有飞行器标签完成 ===`);
         },
         
         // 清空方案ID
         clearPlanId() {
             this.planId = '';
             this.batchPlanId = 1;
             this.status = '已清空方案ID';
         },
         
         // 自动获取航线
         autoGetRoutes() {
             if (this.isConnected && this.websocket && this.planId) {
                 try {
                     const routeMessage = {
                         wsClientType: this.wsClientType || 1,
                         planId: parseInt(this.planId) || 1,
                         schemaRequirementId: parseInt(this.schemaRequirementId) || 815
                     };
                     this.websocket.send(JSON.stringify(routeMessage));
                     this.status = '已自动重新获取航线...';
                 } catch (error) {
                     console.error('自动获取航线失败:', error);
                     this.status = '自动获取航线失败';
                 }
             }
         },
         
         // 清空所有航线（不需要确认）
         clearAllRoutesWithoutConfirm() {
             // 清除所有航线实体
             if (this.routeEntities) {
                 Object.values(this.routeEntities).forEach(entity => {
                     if (entity) this.viewer.entities.remove(entity);
                 });
                 
                 // 清空航线实体引用
                 this.routeEntities = {};
                 
                 this.status = '已清空所有航线';
             }
         },
        
                 clearHistoricalTracks() {
             if (confirm('确定要清除历史轨迹吗？飞机位置将保留')) {
                 // 清除轨迹线实体，但保留飞机实体
                 Object.values(this.trackPolylines).forEach(entity => {
                     if (entity) this.viewer.entities.remove(entity);
                 });
                 
                 // 清空轨迹线引用
                 this.trackPolylines = {};
                 
                 // 清空轨迹数据中的历史点，只保留最新的点
                 this.flightTracks.forEach(track => {
                     if (track.points && track.points.length > 0) {
                         // 只保留最后一个点
                         track.points = [track.points[track.points.length - 1]];
                     }
                 });
                 
                 this.status = '已清除历史轨迹，飞机位置保留';
             }
         },
         
         clearAllRoutes() {
             if (confirm('确定要清除所有航线吗？')) {
                 // 清除所有航线实体
                 if (this.routeEntities) {
                     Object.values(this.routeEntities).forEach(entity => {
                         if (entity) this.viewer.entities.remove(entity);
                     });
                     
                     // 清空航线实体引用
                     this.routeEntities = {};
                     
                     this.status = '已清除所有航线';
                 } else {
                     this.status = '没有航线需要清除';
                 }
             }
         },
         

         
         // 批量设置方案ID
         setBatchPlanId() {
             if (!this.batchPlanId || this.batchPlanId < 1) {
                 alert('请输入有效的方案ID（大于等于1）');
                 return;
             }
             
             // 为所有记录设置方案ID
             this.geojsonRecords.forEach(record => {
                 record.planId = this.batchPlanId;
             });
             
                              // console.log(`已批量设置 ${this.geojsonRecords.length} 条记录的方案ID为: ${this.batchPlanId}`);
             this.status = `已批量设置方案ID为: ${this.batchPlanId}`;
         },
         
         // 从输入框同步方案ID到批量设置
         updateBatchPlanIdFromInput() {
             const planIdValue = parseInt(this.planId);
             if (!planIdValue || planIdValue < 1) {
                 alert('请输入有效的方案ID（大于等于1）');
                 return;
             }
             
             this.batchPlanId = planIdValue;
             
             // 同时更新所有现有记录的方案ID
             this.geojsonRecords.forEach(record => {
                 record.planId = planIdValue;
             });
             
                              // console.log(`已同步方案ID: ${planIdValue}`);
             this.status = `已同步方案ID为: ${planIdValue}`;
         },
         
         // 重新生成记录名称
         regenerateRecordNames() {
             this.geojsonRecords.forEach((record, index) => {
                 record.name = `${this.batchNamePrefix}${index + 1}`;
             });
                              // console.log('已重新生成记录名称');
         },
         
         // 批量设置记录名称
         setBatchNames() {
             if (!this.batchNamePrefix || this.batchNamePrefix.trim() === '') {
                 alert('请输入有效的名称前缀');
                 return;
             }
             
             const prefix = this.batchNamePrefix.trim();
             
             // 为所有记录设置名称
             this.geojsonRecords.forEach((record, index) => {
                 record.name = `${prefix}${index + 1}`;
             });
             
                              // console.log(`已批量设置 ${this.geojsonRecords.length} 条记录的名称前缀为: ${prefix}`);
             this.status = `已批量设置名称前缀为: ${prefix}`;
         },
        
        updateRenderDelay() {
            // 更新渲染间隔
            if (this.renderInterval) {
                clearInterval(this.renderInterval);
            }
            if (this.enableRealTimeUpdate) {
                this.renderInterval = setInterval(() => {
                    this.renderNewPoints();
                }, this.renderDelay);
            }
        },
        
        toggleRealTimeUpdate() {
            if (this.enableRealTimeUpdate) {
                // 启用实时更新
                this.updateRenderDelay();
            } else {
                // 禁用实时更新
                if (this.renderInterval) {
                    clearInterval(this.renderInterval);
                    this.renderInterval = null;
                }
            }
        },
        
        toggleHistoricalTracks() {
            // 更新所有轨迹线的可见性
            Object.keys(this.trackPolylines).forEach(trackId => {
                const polylineEntity = this.trackPolylines[trackId];
                if (polylineEntity) {
                    polylineEntity.show = this.showHistoricalTracks;
                }
            });
            
            // 更新状态信息
            if (this.showHistoricalTracks) {
                this.status = '已显示历史轨迹';
            } else {
                this.status = '已隐藏历史轨迹';
            }
        },
        
                 // 创建飞行器图标
         createColoredDroneIcon(color) {
             // 使用指定的飞行器图片
             return 'http://211.159.175.30:8011/uamtest/image/fly/flane.png';
         },
        
        // 设置当前轨迹（实时模式下简化）
                 setCurrentTrack(trackIndex) {
             if (this.isUpdatingSelection) return; // 防止循环调用
             
             this.isUpdatingSelection = true;
             this.currentTrackIndex = trackIndex;
             if (this.flightTracks[trackIndex]) {
                 this.flightPath = this.flightTracks[trackIndex].points;
                 // 同步更新右边面板的选择
                 this.selectedTrackId = this.flightTracks[trackIndex].id;
                 this.onTrackSelectionChange();
                 this.status = `已选择轨迹${trackIndex + 1}，实时模式运行中`;
                 
                 // 初始化时间轴
                 this.initializeTimeline();
             }
             this.isUpdatingSelection = false;
         },
         
         // ================= 时间轴控制 =================
         initializeTimeline() {
             if (!this.hasValidTrack) return;
             
             const track = this.flightTracks[this.currentTrackIndex];
             if (track.points && track.points.length > 0) {
                 // 计算总时长
                 const lastPoint = track.points[track.points.length - 1];
                 this.totalDuration = lastPoint.timestamp;
                 this.currentTime = 0;
                 this.currentTimePercent = 0;
                 
                 // 停止当前播放
                 this.stopPlayback();
             }
         },
         
         playPause() {
             if (!this.hasValidTrack) return;
             
             if (this.timeMode === 'cesium') {
                 // 方案B：使用Cesium内置时间播放
                 if (this.viewer && this.viewer.clock) {
                     this.viewer.clock.shouldAnimate = !this.viewer.clock.shouldAnimate;
                     this.playing = this.viewer.clock.shouldAnimate;
                     
                     // 更新状态
                     this.status = this.playing ? 'Cesium原生时间播放已启动' : 'Cesium原生时间播放已暂停';
                 }
             } else {
                 // 方案A：使用自定义时间播放
                 if (this.playing) {
                     this.stopPlayback();
                 } else {
                     this.startPlayback();
                 }
                 
                 // 同时控制Cesium内置时钟
                 if (this.viewer && this.viewer.clock) {
                     this.viewer.clock.shouldAnimate = this.playing;
                 }
             }
         },
         
                  startPlayback() {
             if (!this.hasValidTrack) return;
             
             this.playing = true;
             this.playbackStartTime = Date.now();
             
             // 启动Cesium内置时钟
             if (this.viewer && this.viewer.clock) {
                 this.viewer.clock.shouldAnimate = true;
                 this.viewer.clock.multiplier = this.playbackSpeed;
             }
             
             // 使用setInterval，但频率更高，确保平滑
             this.playbackInterval = setInterval(() => {
                 if (!this.playing) return;
                 
                 const now = Date.now();
                 const elapsedTime = (now - this.playbackStartTime) / 1000;
                 
                 // 计算播放进度
                 this.currentTime = (elapsedTime * this.playbackSpeed);
                 
                 if (this.currentTime >= this.totalDuration) {
                     this.currentTime = this.totalDuration;
                     this.stopPlayback();
                     return;
                 }
                 
                 this.currentTimePercent = (this.currentTime / this.totalDuration) * 100;
                 this.updateAircraftPositionForTime();
                 
                 // 调试信息：每2秒输出一次
                 if (Math.floor(this.currentTime) % 2 === 0 && this.currentTime > 0) {
                     // console.log(`播放时间: ${this.currentTime.toFixed(1)}s, 进度: ${this.currentTimePercent.toFixed(1)}%`);
                 }
                 
             }, 50); // 20FPS，平衡性能和流畅度
         },
         
         stopPlayback() {
             this.playing = false;
             if (this.playbackInterval) {
                 clearInterval(this.playbackInterval);
                 this.playbackInterval = null;
             }
             // 重置播放开始时间
             this.playbackStartTime = null;
             
             // 停止Cesium内置时钟
             if (this.viewer && this.viewer.clock) {
                 this.viewer.clock.shouldAnimate = false;
             }
         },
         
         stepForward() {
             if (!this.hasValidTrack) return;
             
             if (this.timeMode === 'cesium') {
                 // 方案B：使用Cesium内置时间播放
                 if (this.viewer && this.viewer.clock) {
                     // 暂停播放，避免拖动时的跳动
                     const wasPlaying = this.viewer.clock.shouldAnimate;
                     this.viewer.clock.shouldAnimate = false;
                     
                     const newTime = Cesium.JulianDate.addSeconds(this.viewer.clock.currentTime, 10, new Cesium.JulianDate());
                     // 确保不超过结束时间
                     if (Cesium.JulianDate.compare(newTime, this.viewer.clock.stopTime) <= 0) {
                         this.viewer.clock.currentTime = newTime;
                     } else {
                         this.viewer.clock.currentTime = this.viewer.clock.stopTime.clone();
                     }
                     
                     // 恢复播放状态
                     this.viewer.clock.shouldAnimate = wasPlaying;
                     this.playing = wasPlaying;
                     
                     this.status = 'Cesium原生时间播放：向前10秒';
                 }
             } else {
                 // 方案A：使用自定义时间播放
                 this.currentTime += 10; // 向前10秒
                 if (this.currentTime > this.totalDuration) {
                     this.currentTime = this.totalDuration;
                 }
                 this.currentTimePercent = (this.currentTime / this.totalDuration) * 100;
                 this.updateAircraftPositionForTime();
                 
                 // 同时控制Cesium内置时钟
                 if (this.viewer && this.viewer.clock) {
                     const newTime = Cesium.JulianDate.addSeconds(this.viewer.clock.startTime, this.currentTime, new Cesium.JulianDate());
                     this.viewer.clock.currentTime = newTime;
                 }
             }
         },
         
         stepBackward() {
             if (!this.hasValidTrack) return;
             
             if (this.timeMode === 'cesium') {
                 // 方案B：使用Cesium内置时间播放
                 if (this.viewer && this.viewer.clock) {
                     // 暂停播放，避免拖动时的跳动
                     const wasPlaying = this.viewer.clock.shouldAnimate;
                     this.viewer.clock.shouldAnimate = false;
                     
                     const newTime = Cesium.JulianDate.addSeconds(this.viewer.clock.currentTime, -10, new Cesium.JulianDate());
                     // 确保不超过开始时间
                     if (Cesium.JulianDate.compare(newTime, this.viewer.clock.startTime) >= 0) {
                         this.viewer.clock.currentTime = newTime;
                     } else {
                         this.viewer.clock.currentTime = this.viewer.clock.startTime.clone();
                     }
                     
                     // 恢复播放状态
                     this.viewer.clock.shouldAnimate = wasPlaying;
                     this.playing = wasPlaying;
                     
                     this.status = 'Cesium原生时间播放：向后10秒';
                 }
             } else {
                 // 方案A：使用自定义时间播放
                 this.currentTime -= 10; // 向后10秒
                 if (this.currentTime < 0) {
                     this.currentTime = 0;
                 }
                 this.currentTimePercent = (this.currentTime / this.totalDuration) * 100;
                 this.updateAircraftPositionForTime();
                 
                 // 同时控制Cesium时钟
                 if (this.viewer && this.viewer.clock) {
                     const newTime = Cesium.JulianDate.addSeconds(this.viewer.clock.startTime, this.currentTime, new Cesium.JulianDate());
                     this.viewer.clock.currentTime = newTime;
                 }
             }
         },
         
         resetTime() {
             if (!this.hasValidTrack) return;
             
             if (this.timeMode === 'cesium') {
                 // 方案B：使用Cesium内置时间播放
                 if (this.viewer && this.viewer.clock) {
                     // 暂停播放，避免拖动时的跳动
                     const wasPlaying = this.viewer.clock.shouldAnimate;
                     this.viewer.clock.shouldAnimate = false;
                     
                     this.viewer.clock.currentTime = this.viewer.clock.startTime.clone();
                     
                     // 恢复播放状态
                     this.viewer.clock.shouldAnimate = wasPlaying;
                     this.playing = wasPlaying;
                     
                     this.status = 'Cesium原生时间播放：已重置到开始时间';
                 }
             } else {
                 // 方案A：使用自定义时间播放
                 this.currentTime = 0;
                 this.currentTimePercent = 0;
                 this.updateAircraftPositionForTime();
                 
                 // 同时控制Cesium内置时钟
                 if (this.viewer && this.viewer.clock) {
                     this.viewer.clock.currentTime = this.viewer.clock.startTime.clone();
                 }
             }
         },
         
         seekToTime() {
             if (!this.hasValidTrack) return;
             
             // 防抖处理，避免频繁更新
             if (this.seekDebounceTimer) {
                 clearTimeout(this.seekDebounceTimer);
             }
             
             this.seekDebounceTimer = setTimeout(() => {
                 this.performSeek();
             }, 50); // 50ms防抖
         },
         
         performSeek() {
             if (!this.hasValidTrack) return;
             
             if (this.timeMode === 'cesium') {
                 // 方案B：使用Cesium内置时间播放
                 if (this.viewer && this.viewer.clock) {
                     // 暂停播放，避免拖动时的跳动
                     const wasPlaying = this.viewer.clock.shouldAnimate;
                     this.viewer.clock.shouldAnimate = false;
                     
                     const totalSeconds = Cesium.JulianDate.secondsDifference(this.viewer.clock.stopTime, this.viewer.clock.startTime);
                     const targetSeconds = (this.currentTimePercent / 100) * totalSeconds;
                     const newTime = Cesium.JulianDate.addSeconds(this.viewer.clock.startTime, targetSeconds, new Cesium.JulianDate());
                     this.viewer.clock.currentTime = newTime;
                     
                     // 恢复播放状态
                     this.viewer.clock.shouldAnimate = wasPlaying;
                     this.playing = wasPlaying;
                 }
             } else {
                 // 方案A：使用自定义时间播放
                 this.currentTime = (this.currentTimePercent / 100) * this.totalDuration;
                 
                 // 重置播放开始时间，确保从新位置开始播放
                 if (this.playing) {
                     this.playbackStartTime = Date.now() - (this.currentTime / this.playbackSpeed) * 1000;
                 }
                 
                 // 同步Cesium内置时钟时间
                 if (this.viewer && this.viewer.clock) {
                     const newTime = Cesium.JulianDate.addSeconds(this.viewer.clock.startTime, this.currentTime, new Cesium.JulianDate());
                     this.viewer.clock.currentTime = newTime;
                 }
                 
                 this.updateAircraftPositionForTime();
             }
         },
         
         updatePlaybackSpeed() {
             // 更新Cesium内置时钟的播放速度
             if (this.viewer && this.viewer.clock) {
                 this.viewer.clock.multiplier = this.playbackSpeed;
             }
             
             // 如果使用自定义播放，需要重新计算播放开始时间
             if (this.timeMode === 'custom' && this.playing) {
                 this.playbackStartTime = Date.now() - (this.currentTime / this.playbackSpeed) * 1000;
             }
         },
         
         applyTimeMode() {
             if (this.timeMode === 'cesium') {
                 // 方案B：使用Cesium内置时间播放
                 this.stopPlayback(); // 停止自定义播放
                 
                 // 重新创建带时间属性的轨迹实体
                 this.recreateTracksWithTimeProperties();
                 
                 // 调试信息
                 console.log('方案B：已重新创建带时间属性的轨迹实体');
                 console.log('轨迹数量:', this.flightTracks.length);
                 
                 // 显示每个轨迹的时间戳信息
                 this.flightTracks.forEach((track, index) => {
                     if (track.points && track.points.length > 0) {
                         const timestamps = track.points.map(p => p.timestamp_absolute || p.ts || p.timestamp).filter(t => t !== undefined);
                         if (timestamps.length > 0) {
                             const minTimestamp = Math.min(...timestamps);
                             const maxTimestamp = Math.max(...timestamps);
                             console.log(`轨迹${index + 1} (${track.id}):`, {
                                 点数: track.points.length,
                                 时间范围: `${new Date(minTimestamp).toLocaleString()} - ${new Date(maxTimestamp).toLocaleString()}`,
                                 时长: `${(maxTimestamp - minTimestamp) / 1000}秒`
                             });
                         }
                     }
                 });
                 
                 console.log('时钟配置:', {
                     startTime: this.viewer.clock.startTime,
                     stopTime: this.viewer.clock.stopTime,
                     currentTime: this.viewer.clock.currentTime,
                     multiplier: this.viewer.clock.multiplier
                 });
                 
                 // 配置Cesium时钟
                 if (this.viewer && this.viewer.clock) {
                     // 计算所有轨迹的时间范围
                     let globalMinTimestamp = Infinity;
                     let globalMaxTimestamp = -Infinity;
                     
                     this.flightTracks.forEach(track => {
                         if (track.points && track.points.length > 0) {
                             const timestamps = track.points.map(p => p.timestamp_absolute || p.ts || p.timestamp).filter(t => t !== undefined);
                             if (timestamps.length > 0) {
                                 const minTimestamp = Math.min(...timestamps);
                                 const maxTimestamp = Math.max(...timestamps);
                                 globalMinTimestamp = Math.min(globalMinTimestamp, minTimestamp);
                                 globalMaxTimestamp = Math.max(globalMaxTimestamp, maxTimestamp);
                             }
                         }
                     });
                     
                     // 如果没有有效的时间戳，使用默认时间范围
                     if (globalMinTimestamp === Infinity || globalMaxTimestamp === -Infinity) {
                         const startTime = Cesium.JulianDate.fromDate(new Date('2025-08-30T16:00:00Z'));
                         const stopTime = Cesium.JulianDate.fromDate(new Date('2025-08-31T14:00:00Z'));
                         
                         this.viewer.clock.startTime = startTime;
                         this.viewer.clock.stopTime = stopTime;
                         this.viewer.clock.currentTime = startTime;
                     } else {
                         // 使用实际的时间范围
                         const startTime = Cesium.JulianDate.fromDate(new Date(globalMinTimestamp));
                         const stopTime = Cesium.JulianDate.fromDate(new Date(globalMaxTimestamp));
                         
                         this.viewer.clock.startTime = startTime;
                         this.viewer.clock.stopTime = stopTime;
                         this.viewer.clock.currentTime = startTime;
                         
                         // 设置时间轴范围
                         this.viewer.timeline.zoomTo(startTime, stopTime);
                     }
                     
                                      this.viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
                 this.viewer.clock.multiplier = this.playbackSpeed;
                 this.viewer.clock.shouldAnimate = true; // 自动开始播放
                 this.playing = true; // 更新播放状态
                 }
                 
                 this.status = '已切换到Cesium内置时间播放模式，自动开始播放';
             } else {
                 // 方案A：使用自定义时间播放
                 if (this.viewer && this.viewer.clock) {
                     this.viewer.clock.shouldAnimate = false; // 停止Cesium时钟
                 }
                 
                 this.status = '已切换到自定义时间播放模式';
             }
                  },
         
         recreateTracksWithTimeProperties() {
             // 清除现有的轨迹实体
             this.clearAllTracks();
             
             // 重新创建带时间属性的轨迹
             this.flightTracks.forEach(track => {
                 if (track.points && track.points.length > 0) {
                     this.createTrackWithTimeProperties(track);
                 }
             });
         },
         
         createTrackWithTimeProperties(track) {
             if (!track.points || track.points.length === 0) return;
             
             // 创建时间点数组
             const timePoints = [];
             const positions = [];
             
             // 找到轨迹的时间范围
             const timestamps = track.points.map(p => p.timestamp_absolute || p.ts || p.timestamp).filter(t => t !== undefined);
             if (timestamps.length === 0) {
                 console.warn('轨迹没有有效的时间戳:', track.id);
                 return;
             }
             
             const minTimestamp = Math.min(...timestamps);
             const maxTimestamp = Math.max(...timestamps);
             
             // 设置Cesium时钟的时间范围
             const startTime = Cesium.JulianDate.fromDate(new Date(minTimestamp));
             const stopTime = Cesium.JulianDate.fromDate(new Date(maxTimestamp));
             
             this.viewer.clock.startTime = startTime;
             this.viewer.clock.stopTime = stopTime;
             this.viewer.clock.currentTime = startTime;
             
             // 设置时间轴范围
             this.viewer.timeline.zoomTo(startTime, stopTime);
             
             track.points.forEach((point, index) => {
                 // 使用实际的时间戳
                 const timestamp = point.timestamp_absolute || point.ts || point.timestamp;
                 if (timestamp !== undefined) {
                     const time = Cesium.JulianDate.fromDate(new Date(timestamp));
                     timePoints.push(time);
                     positions.push(Cesium.Cartesian3.fromDegrees(
                         point.longitude, 
                         point.latitude, 
                         point.altitude || 0
                     ));
                 }
             });
             
             // 创建带时间属性的轨迹线
             const trackEntity = this.viewer.entities.add({
                 id: `track-${track.id}`,
                 name: this.getTaskNameByTrackId(track.id),
                 path: {
                     positions: positions,
                     material: Cesium.Color.CYAN.withAlpha(0.8),
                     width: 3,
                     show: true,
                     leadTime: 0,
                     trailTime: 0,
                     // 关键：设置时间属性
                     time: timePoints
                 }
             });
             
             // 创建带时间属性的飞机实体
             const aircraftEntity = this.viewer.entities.add({
                 id: `aircraft-${track.id}`,
                 name: this.getTaskNameByTrackId(track.id),
                 position: positions[0],
                 model: {
                     uri: 'https://raw.githubusercontent.com/AnalyticalGraphicsInc/cesium/master/Apps/SampleData/models/CesiumAir/Cesium_Air.glb',
                     minimumPixelSize: 64,
                     maximumScale: 20000,
                     scale: 1.0
                 },
                 label: {
                     text: this.getTaskNameByTrackId(track.id),
                     font: '14pt monospace',
                     style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                     outlineWidth: 2,
                     verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                     pixelOffset: new Cesium.Cartesian2(0, -9),
                     show: true
                 },
                 // 关键：设置时间属性
                 availability: new Cesium.TimeIntervalCollection([
                     new Cesium.TimeInterval({
                         start: timePoints[0],
                         stop: timePoints[timePoints.length - 1]
                     })
                 ]),
                 // 设置方向控制，让飞机朝向飞行方向
                 orientation: new Cesium.VelocityOrientationProperty(positions)
             });
             
             // 存储实体引用
             this.trackEntities[track.id] = trackEntity;
             this.aircraftEntities[track.id] = aircraftEntity;
         },
         
         updateAircraftPositionForTime() {
             if (!this.hasValidTrack) return;
             
             // 方案B不需要手动更新飞机位置，Cesium会自动处理
             if (this.timeMode === 'cesium') {
                 return;
             }
             
             const track = this.flightTracks[this.currentTrackIndex];
             // 直接使用当前时间（秒），不需要转换为毫秒
             const targetTime = this.currentTime;
              
              // 找到当前时间对应的点
              const currentPoint = this.getCurrentPoint(track, targetTime);
              
              // 同步Cesium内置时钟时间
              if (this.viewer && this.viewer.clock) {
                  const newTime = Cesium.JulianDate.addSeconds(this.viewer.clock.startTime, this.currentTime, new Cesium.JulianDate());
                  this.viewer.clock.currentTime = newTime;
              }
              
              if (currentPoint) {
                  // 更新飞机位置
                  const aircraftEntity = this.aircraftEntities[track.id];
                  if (aircraftEntity) {
                      try {
                          // 直接设置飞机位置，不使用平滑插值，确保匀速移动
                          aircraftEntity.position = Cesium.Cartesian3.fromDegrees(
                              currentPoint.longitude,
                              currentPoint.latitude,
                              currentPoint.altitude
                          );
                          
                          // 计算并更新飞行方向
                          const heading = this.calculateAircraftHeading(track, currentPoint, targetTime);
                          if (heading !== null) {
                              const headingRadians = Cesium.Math.toRadians(heading);
                              // 检查实体类型，避免设置不存在的属性
                              if (aircraftEntity.billboard) {
                                  aircraftEntity.billboard.rotation = headingRadians;
                              }
                              
                              // 更新飞行方向显示
                              const direction = this.getHeadingDirection(heading);
                              this.headingText = `${heading.toFixed(1)}° (${direction})`;
                          } else {
                              this.headingText = '-';
                          }
                          
                          // 更新标签（使用任务名称）
                          const taskName = this.getTaskNameByTrackId(track.id);
                          if (aircraftEntity.label) {
                              aircraftEntity.label.text = taskName;
                          }
                      } catch (e) {
                          console.warn('更新飞机位置失败:', currentPoint, e);
                      }
                  }
                  
                  // 更新轨迹线颜色（时间轴模式）
                  this.updateTrackPolylineColor(track.id, track, targetTime);
                  
                  // 更新位置信息显示
                  const trackName = track.aircraft_type || `轨迹${this.currentTrackIndex + 1}`;
                  const batteryInfo = currentPoint.battery !== undefined ? ` - 电池: ${Math.round(currentPoint.battery * 100)}%` : '';
                  
                  this.posText = `${trackName}: ${currentPoint.longitude.toFixed(4)}, ${currentPoint.latitude.toFixed(4)}`;
                  this.segText = `时间轴位置 - 高度: ${currentPoint.altitude}m${batteryInfo}`;
              }
          },

                 onTick() {
             // 只有在非播放模式下才显示实时位置
             if (!this.playing && this.flightPath && this.flightPath.length > 0) {
                 const currentTime = Date.now();
                 const track = this.flightTracks[this.currentTrackIndex];
                 const relativeTime = (currentTime - track.startTime) / 1000;
                 const currentPoint = this.getCurrentPoint(track, relativeTime);
                 
                 if (currentPoint) {
                     const track = this.flightTracks[this.currentTrackIndex];
                     const trackName = track.aircraft_type || `轨迹${this.currentTrackIndex + 1}`;
                     const batteryInfo = currentPoint.battery !== undefined ? ` - 电池: ${Math.round(currentPoint.battery * 100)}%` : '';
                     
                     this.posText = `${trackName}: ${currentPoint.longitude.toFixed(4)}, ${currentPoint.latitude.toFixed(4)}`;
                     this.segText = `实时位置 - 高度: ${currentPoint.altitude}m${batteryInfo}`;
                 }
             }
         },

         
         // ================= 多边形绘制 =================
         startDrawPolygon() {
             if (this.drawing) return;
             this.drawing = true;
             this.drawPositions = [];
             if (this.drawPolygonEntity) { this.viewer.entities.remove(this.drawPolygonEntity); this.drawPolygonEntity = null; }
             if (this.drawOutlineEntity) { this.viewer.entities.remove(this.drawOutlineEntity); this.drawOutlineEntity = null; }
             if (this.drawHandler) { this.drawHandler.destroy(); this.drawHandler = null; }
             this.drawHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.canvas);

             // 左键点击：落点
             this.drawHandler.setInputAction((movement) => {
                 const cart = this.getCartesianFromMouse(movement.position);
                 if (!cart) return;
                 this.drawPositions.push(cart);
                 this.ensureDynamicPolygon();
             }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

             // 右键点击：完成绘制
             this.drawHandler.setInputAction((movement) => {
                 if (this.drawing && this.drawPositions.length >= 3) {
                     this.finishDrawPolygon();
                 }
             }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);

             // 移动：动态预览末点
             this.drawHandler.setInputAction((movement) => {
                 if (!this.drawing || this.drawPositions.length === 0) return;
                 const cart = this.getCartesianFromMouse(movement.endPosition);
                 if (!cart) return;
                 const temp = this.drawPositions.slice();
                 temp.push(cart);
                 this.updateDynamicPolygon(temp);
             }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
         },
         finishDrawPolygon() {
             if (!this.drawing) return;
             this.drawing = false;
             if (this.drawHandler) { this.drawHandler.destroy(); this.drawHandler = null; }
             // 最终以 drawPositions 生成实体
             if (this.drawPositions.length < 3) return;
             this.updateDynamicPolygon(this.drawPositions, true);
         },
         clearDrawPolygon() {
             this.drawing = false;
             this.drawPositions = [];
             this.polygonGeoJSON = null; // 清除GeoJSON
             
             // 清除所有记录及其对应的实体
             for (const record of this.geojsonRecords) {
                 if (record.polygonEntity) {
                     this.viewer.entities.remove(record.polygonEntity);
                 }
                 if (record.outlineEntity) {
                     this.viewer.entities.remove(record.outlineEntity);
                 }
             }
             this.geojsonRecords = []; // 清空记录数组
             
             if (this.drawHandler) { this.drawHandler.destroy(); this.drawHandler = null; }
             if (this.drawPolygonEntity) { this.viewer.entities.remove(this.drawPolygonEntity); this.drawPolygonEntity = null; }
             if (this.drawOutlineEntity) { this.viewer.entities.remove(this.drawOutlineEntity); this.drawOutlineEntity = null; }
         },
         getCartesianFromMouse(winPos) {
             if (!winPos) return null;
             const scene = this.viewer.scene;
             let cartesian = null;
             if (scene.pickPositionSupported) {
                 cartesian = scene.pickPosition(winPos);
             }
             if (!Cesium.defined(cartesian)) {
                 const ray = this.viewer.camera.getPickRay(winPos);
                 cartesian = scene.globe.pick(ray, scene);
             }
             return cartesian || null;
         },
         ensureDynamicPolygon() {
             if (this.drawPolygonEntity) return;
             const hierarchyCb = new Cesium.CallbackProperty(() => {
                 const list = (this._previewPositions || this.drawPositions);
                 if (!list || list.length < 3) return null;
                 return new Cesium.PolygonHierarchy(list);
             }, false);
             this.drawPolygonEntity = this.viewer.entities.add({
                 polygon: {
                     hierarchy: hierarchyCb,
                     material: Cesium.Color.fromBytes(255, 165, 0, 100), // 橙色半透明
                     perPositionHeight: true,
                     outline: false
                 }
             });
             this.drawOutlineEntity = this.viewer.entities.add({
                 polyline: {
                     positions: new Cesium.CallbackProperty(() => {
                         const list = (this._previewPositions || this.drawPositions);
                         if (!list || list.length < 2) return [];
                         return list.concat(list[0]); // 闭合预览
                     }, false),
                     width: 2,
                     material: Cesium.Color.ORANGE
                 }
             });
         },
         updateDynamicPolygon(positions, finalize = false) {
             this._previewPositions = positions;
             if (finalize) {
                 // 清除预览实体
                 if (this.drawPolygonEntity) { this.viewer.entities.remove(this.drawPolygonEntity); this.drawPolygonEntity = null; }
                 if (this.drawOutlineEntity) { this.viewer.entities.remove(this.drawOutlineEntity); this.drawOutlineEntity = null; }
                 this._previewPositions = null;
                 
                 // 生成GeoJSON（会自动创建实体并添加到记录中）
                 this.generateGeoJSON(positions);
             }
         },
         
         // 生成GeoJSON
         generateGeoJSON(positions) {
             if (!positions || positions.length < 3) {
                 this.polygonGeoJSON = null;
                 return;
             }
             
             // 将Cartesian3坐标转换为经纬度
             const coordinates = [];
             for (const pos of positions) {
                 const cartographic = Cesium.Cartographic.fromCartesian(pos);
                 const longitude = Cesium.Math.toDegrees(cartographic.longitude);
                 const latitude = Cesium.Math.toDegrees(cartographic.latitude);
                 coordinates.push([longitude, latitude]);
             }
             
             // 闭合多边形（首尾相连）
             if (coordinates.length > 0) {
                 coordinates.push(coordinates[0]);
             }
             
             const geojson = {
                 type: "Feature",
                 properties: {
                     name: "绘制多边形",
                     timestamp: new Date().toISOString()
                 },
                 geometry: {
                     type: "Polygon",
                     coordinates: [coordinates]
                 }
             };
             
             this.polygonGeoJSON = JSON.stringify(geojson, null, 2);
             
             // 添加到记录表格
             this.geojsonRecords.push({
                 timestamp: new Date(),
                 pointCount: positions.length,
                 name: `${this.batchNamePrefix}${this.geojsonRecords.length + 1}`, // 自动生成名称
                 planId: this.planId || 1, // 使用从轨迹获取的方案ID，默认为1
                 geojson: geojson,
                 geojsonString: this.polygonGeoJSON,
                 polygonEntity: null, // 将在下面设置
                 outlineEntity: null  // 将在下面设置
             });
             
             // 获取刚添加的记录索引
             const recordIndex = this.geojsonRecords.length - 1;
             
             // 创建多边形实体并保存引用
             const polygonEntity = this.viewer.entities.add({
                 polygon: {
                     hierarchy: new Cesium.PolygonHierarchy(positions),
                     material: Cesium.Color.fromBytes(255, 165, 0, 100),
                     perPositionHeight: true
                 }
             });
             
             const outlineEntity = this.viewer.entities.add({
                 polyline: {
                     positions: positions.concat(positions[0]),
                     width: 2,
                     material: Cesium.Color.ORANGE
                 }
             });
             
             // 保存实体引用到记录中
             this.geojsonRecords[recordIndex].polygonEntity = polygonEntity;
             this.geojsonRecords[recordIndex].outlineEntity = outlineEntity;
         },
         
         // 复制GeoJSON到剪贴板
         copyGeoJSON() {
             if (!this.polygonGeoJSON) return;
             
             navigator.clipboard.writeText(this.polygonGeoJSON).then(() => {
                 // 临时显示复制成功提示
                 const btn = event.target;
                 const originalText = btn.textContent;
                 btn.textContent = '已复制';
                 btn.style.background = '#4CAF50';
                 setTimeout(() => {
                     btn.textContent = originalText;
                     btn.style.background = '#2196F3';
                 }, 1000);
             }).catch(err => {
                 console.error('复制失败:', err);
                 // 降级方案：使用传统方法
                 const textArea = document.createElement('textarea');
                 textArea.value = this.polygonGeoJSON;
                 document.body.appendChild(textArea);
                 textArea.select();
                 document.execCommand('copy');
                 document.body.removeChild(textArea);
                 
                 const btn = event.target;
                 const originalText = btn.textContent;
                 btn.textContent = '已复制';
                 btn.style.background = '#4CAF50';
                 setTimeout(() => {
                     btn.textContent = originalText;
                     btn.style.background = '#2196F3';
                 }, 1000);
             });
         },
         
         // 格式化时间显示
         formatTime(date) {
             if (!date) return '-';
             const d = new Date(date);
             return `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}:${d.getSeconds().toString().padStart(2, '0')}`;
         },
         
         // 复制记录中的GeoJSON
         copyRecordGeoJSON(index) {
             const record = this.geojsonRecords[index];
             if (!record) return;
             
             navigator.clipboard.writeText(record.geojsonString).then(() => {
                 // 临时显示复制成功提示
                 const btn = event.target;
                 const originalText = btn.textContent;
                 btn.textContent = '已复制';
                 btn.style.background = '#4CAF50';
                 setTimeout(() => {
                     btn.textContent = originalText;
                     btn.style.background = '#2196F3';
                 }, 1000);
             }).catch(err => {
                 console.error('复制失败:', err);
                 // 降级方案
                 const textArea = document.createElement('textarea');
                 textArea.value = record.geojsonString;
                 document.body.appendChild(textArea);
                 textArea.select();
                 document.execCommand('copy');
                 document.body.removeChild(textArea);
                 
                 const btn = event.target;
                 const originalText = btn.textContent;
                 btn.textContent = '已复制';
                 btn.style.background = '#4CAF50';
                 setTimeout(() => {
                     btn.textContent = originalText;
                     btn.style.background = '#2196F3';
                 }, 1000);
             });
         },
         
         // 查看记录中的完整GeoJSON
         viewRecordGeoJSON(index) {
             const record = this.geojsonRecords[index];
             if (!record) return;
             
             // 显示在弹窗中
             alert(`GeoJSON记录 ${index + 1}:\n\n${record.geojsonString}`);
         },
         
         // 删除记录
         deleteRecord(index) {
             if (confirm(`确定要删除第 ${index + 1} 条记录吗？`)) {
                 const record = this.geojsonRecords[index];
                 if (record) {
                     // 删除对应的多边形实体
                     if (record.polygonEntity) {
                         this.viewer.entities.remove(record.polygonEntity);
                     }
                     if (record.outlineEntity) {
                         this.viewer.entities.remove(record.outlineEntity);
                     }
                 }
                 // 从数组中删除记录
                 this.geojsonRecords.splice(index, 1);
                 
                 // 重新生成剩余记录的名称
                 this.regenerateRecordNames();
             }
         },
         
         // 发送局部避障消息
         sendLocalAvoidance(record) {
             if (!this.isConnected) {
                 alert('WebSocket未连接，无法发送局部避障消息');
                 return;
             }
             
             try {
                 // 从GeoJSON中提取坐标
                 let coordinates = [];
                 if (record.geojson && record.geojson.geometry && record.geojson.geometry.coordinates) {
                     coordinates = record.geojson.geometry.coordinates[0]; // 取第一个环的坐标
                 } else {
                     throw new Error('无法从记录中提取坐标数据');
                 }
                 
                 // 构造局部避障消息
                 const localAvoidanceMessage = {
                     wsClientType: "2",
                     planId: record.planId || 1, // 使用记录中的方案ID，默认为1
                     polygon: {
                         type: "Feature",
                         properties: {
                             name: record.name || "绘制多边形", // 使用记录中的名称
                             timestamp: new Date().toISOString()
                         },
                         geometry: {
                             type: "Polygon",
                             coordinates: [coordinates]
                         }
                     }
                 };
                 
                 // 发送WebSocket消息
                 this.websocket.send(JSON.stringify(localAvoidanceMessage));
                 
                 // console.log('已发送局部避障消息:', localAvoidanceMessage);
                 this.status = `已发送局部避障消息 (记录: ${record.geojson.properties?.name || '未命名'})`;
                 
             } catch (error) {
                 console.error('发送局部避障消息失败:', error);
                 this.status = '发送局部避障消息失败';
                 alert('发送局部避障消息失败: ' + error.message);
             }
         }
    }
}).mount('#app');
</script>
</body>
</html>
    